您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
   
 
 
     
   
 订阅
  捐助
AHOOK API (一)——HOOK基础+一个鼠标钩子实例
 
来自于:工匠若水 发布于 2016-2-16
  3401  次浏览      16
 

0x00 起因

最近在做毕业设计,有一个功能是需要实现对剪切板的监控和进程的防终止保护。原本想从内核层实现,但没有头绪。最后决定从调用层入手,即采用HOOK API的技术来挂钩相应的API,从而实现预期的功能。在这样的需求下,就开始学习了HOOK API。

0x01什么是HOOK API

HOOK(钩子,挂钩)是一种实现Windows平台下类似于中断的机制[24]。HOOK机制允许应用程序拦截并处理Windows消息或指定事件,当指定的消息发出后,HOOK程序就可以在消息到达目标窗口之前将其捕获,从而得到对消息的控制权,进而可以对该消息进行处理或修改,加入我们所需的功能。钩子按使用范围分,可分为线程钩子和系统钩子,其中,系统钩子具有相当大的功能,几乎可以实现对所有Windows消息的拦截、处理和监控。这项技术涉及到两个重要的API,一个是SetWindowsHookEx,安装钩子;另一个是UnHookWindowsHookEx,卸载钩子。

本文使用的HOOK API技术,是指截获系统或进程对某个API函数的调用,使得API的执行流程转向我们指定的代码段,从而实现我们所需的功能。Windows下的每个进程均拥有自己的地址空间,并且进程只能调用其地址空间内的函数,因此HOOK API尤为关键的一步是,设法将自己的代码段注入到目标进程中,才能进一步实现对该进程调用的API进行拦截。然而微软并没有提供HOOK API的调用接口,这就需要开发者自己编程实现,大家所熟知的防毒软件、防火墙软件等均采用HOOK API实现。

一般来说,HOOK API由两个组成部分,即实现HOOK API的DLL文件,和启动注入的主调程序。本文采用HOOK API 技术对剪切板相关的API 函数进行拦截,从而实现对剪切板内容的监控功能,同样使用该技术实现进程防终止功能。其中DLL文件支持HOOK API的实现,而主调客户端程序将在初始化时把带有HOOK API功能的DLL随着鼠标钩子的加载注入到目标进程中,这里的鼠标钩子属于系统钩子。

0x02 钩子的类型

1、按事件分类

有如下的几种常用类型

(1) 键盘钩子和低级键盘钩子可以监视各种键盘消息。

(2) 鼠标钩子和低级鼠标钩子可以监视各种鼠标消息。

(3) 外壳钩子可以监视各种Shell事件消息。比如启动和关闭应用程序。

(4) 日志钩子可以记录从系统消息队列中取出的各种事件消息。

(5) 窗口过程钩子监视所有从系统消息队列发往目标窗口的消息。

此外,还有一些特定事件的钩子提供给我们使用,不一一列举。

下面描述常用的Hook类型:

1、WH_CALLWNDPROC和WH_CALLWNDPROCRET Hooks

WH_CALLWNDPROC和WH_CALLWNDPROCRET Hooks使你可以监视发送到窗口过程的消息。系统在消息发送到接收窗口过程之前调用WH_CALLWNDPROC Hook子程,并且在窗口过程处理完消息之后调用WH_CALLWNDPROCRET Hook子程。WH_CALLWNDPROCRET Hook传递指针到CWPRETSTRUCT结构,再传递到Hook子程。CWPRETSTRUCT结构包含了来自处理消息的窗口过程的返回值,同样也包括了与这个消息关联的消息参数。

2、WH_CBT Hook

在以下事件之前,系统都会调用WH_CBT Hook子程,这些事件包括:

1. 激活,建立,销毁,最小化,最大化,移动,改变尺寸等窗口事件;

2. 完成系统指令;

3. 来自系统消息队列中的移动鼠标,键盘事件;

4. 设置输入焦点事件;

5. 同步系统消息队列事件。

Hook子程的返回值确定系统是否允许或者防止这些操作中的一个。

3、WH_DEBUG Hook

在系统调用系统中与其他Hook关联的Hook子程之前,系统会调用WH_DEBUG Hook子程。你可以使用这个Hook来决定是否允许系统调用与其他Hook关联的Hook子程。

4、WH_FOREGROUNDIDLE Hook

当应用程序的前台线程处于空闲状态时,可以使用WH_FOREGROUNDIDLE Hook执行低优先级的任务。当应用程序的前台线程大概要变成空闲状态时,系统就会调用WH_FOREGROUNDIDLE Hook子程。

5、WH_GETMESSAGE Hook

应用程序使用WH_GETMESSAGE Hook来监视从GetMessage or PeekMessage函数返回的消息。你可以使用WH_GETMESSAGE Hook去监视鼠标和键盘输入,以及其他发送到消息队列中的消息。

6、WH_JOURNALPLAYBACK Hook

WH_JOURNALPLAYBACK Hook使应用程序可以插入消息到系统消息队列。可以使用这个Hook回放通过使用WH_JOURNALRECORD Hook记录下来的连续的鼠标和键盘事件。只要WH_JOURNALPLAYBACK Hook已经安装,正常的鼠标和键盘事件就是无效的。WH_JOURNALPLAYBACK Hook是全局Hook,它不能象线程特定Hook一样使用。WH_JOURNALPLAYBACK Hook返回超时值,这个值告诉系统在处理来自回放Hook当前消息之前需要等待多长时间(毫秒)。这就使Hook可以控制实时事件的回放。WH_JOURNALPLAYBACK是system-wide local hooks,它們不會被注射到任何行程位址空間。(估计按键精灵是用这个hook做的)

7、WH_JOURNALRECORD Hook

WH_JOURNALRECORD Hook用来监视和记录输入事件。典型的,可以使用这个Hook记录连续的鼠标和键盘事件,然后通过使用WH_JOURNALPLAYBACK Hook来回放。WH_JOURNALRECORD Hook是全局Hook,它不能象线程特定Hook一样使用。WH_JOURNALRECORD是system-wide local hooks,它們不會被注射到任何行程位址空間。

8、WH_KEYBOARD Hook

在应用程序中,WH_KEYBOARD Hook用来监视WM_KEYDOWN and WM_KEYUP消息,这些消息通过GetMessage or PeekMessage function返回。可以使用这个Hook来监视输入到消息队列中的键盘消息。

9、WH_KEYBOARD_LL Hook

WH_KEYBOARD_LL Hook监视输入到线程消息队列中的键盘消息。

10、WH_MOUSE Hook

WH_MOUSE Hook监视从GetMessage 或者 PeekMessage 函数返回的鼠标消息。使用这个Hook监视输入到消息队列中的鼠标消息。

11、WH_MOUSE_LL Hook

WH_MOUSE_LL Hook监视输入到线程消息队列中的鼠标消息。

12、WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks

WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以监视菜单,滚动条,消息框,对话框消息并且发现用户使用ALT+TAB or ALT+ESC 组合键切换窗口。WH_MSGFILTER Hook只能监视传递到菜单,滚动条,消息框的消息,以及传递到通过安装了Hook子程的应用程序建立的对话框的消息。WH_SYSMSGFILTER Hook监视所有应用程序消息。WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以在模式循环期间过滤消息,这等价于在主消息循环中过滤消息。通过调用CallMsgFilter function可以直接的调用WH_MSGFILTER Hook。通过使用这个函数,应用程序能够在模式循环期间使用相同的代码去过滤消息,如同在主消息循环里一样。

13、WH_SHELL Hook

外壳应用程序可以使用WH_SHELL Hook去接收重要的通知。当外壳应用程序是激活的并且当顶层窗口建立或者销毁时,系统调用WH_SHELL Hook子程。

WH_SHELL 共有5钟情況:

只要有个top-level、unowned 窗口被产生、起作用、或是被摧毁;

当Taskbar需要重画某个按钮;

当系统需要显示关于Taskbar的一个程序的最小化形式;

当目前的键盘布局状态改变;

当使用者按Ctrl+Esc去执行Task Manager(或相同级别的程序)。

按照惯例,外壳应用程序都不接收WH_SHELL消息。所以,在应用程序能够接收WH_SHELL消息之前,应用程序必须调用SystemParametersInfo function注册它自己。

以上是13种常用的hook类型!

2、按使用范围分类

主要有线程钩子和系统钩子:

(1) 线程钩子监视指定线程的事件消息。

(2) 系统钩子监视系统中的所有线程的事件消息。因为系统钩子会影响系统中所有的应用程序,所以钩子函数必须放在独立的动态链接库(DLL)

中。这是系统钩子和线程钩子很大的不同之处。

几点需要说明的地方:

(1) 如果对于同一事件(如鼠标消息)既安装了线程钩子又安装了系统钩子,那么系统会自动先调用线程钩子,然后调用系统钩子。

(2) 对同一事件消息可安装多个钩子处理过程,这些钩子处理过程形成了钩子链。当前钩子处理结束后应把钩子信息传递给下一个钩子函数。而且最近安装的钩子放在链的开始,而最早安装的钩子放在最后,也就是后加入的先获得控制权。

(3) 钩子特别是系统钩子会消耗消息处理时间,降低系统性能。只有在必要的时候才安装钩子,在使用完毕后要及时卸载。

0x03编写钩子程序

编写钩子程序的步骤分为三步:定义钩子函数、安装钩子和卸载钩子。

1.定义钩子函数

钩子函数是一种特殊的回调函数。钩子监视的特定事件发生后,系统会调用钩子函数进行处理。不同事件的钩子函数的形式是各不相同的。下面以鼠标钩子函数举例说明钩子函数的原型:

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

参数wParam和 lParam包含所钩消息的信息,比如鼠标位置、状态,键盘按键等。nCode包含有关消息本身的信息,比如是否从消息队列中移出。

我们先在钩子函数中实现自定义的功能,然后调用函数 CallNextHookEx.把钩子信息传递给钩子链的下一个钩子函数。CallNextHookEx.的原型如下:

LRESULT CallNextHookEx( HHOOK hhk, int nCode, WPARAM wParam, LPARAM lParam )

参数 hhk是钩子句柄。nCode、wParam和lParam 是钩子函数。

当然也可以通过直接返回TRUE来丢弃该消息,就阻止了该消息的传递。

2.安装钩子

在程序初始化的时候,调用函数SetWindowsHookEx安装钩子。其函数原型为:

HHOOK SetWindowsHookEx( int idHook,HOOKPROC lpfn, INSTANCE hMod,DWORD dwThreadId )

参数idHook表示钩子类型,它是和钩子函数类型一一对应的。比如,WH_KEYBOARD表示安装的是键盘钩子,WH_MOUSE表示是鼠标钩子等等。

Lpfn是钩子函数的地址。

HMod是钩子函数所在的实例的句柄。对于线程钩子,该参数为NULL;对于系统钩子,该参数为钩子函数所在的DLL句柄。

dwThreadId 指定钩子所监视的线程的线程号。对于全局钩子,该参数为NULL。

SetWindowsHookEx返回所安装的钩子句柄。

3.卸载钩子

当不再使用钩子时,必须及时卸载。简单地调用函数:

BOOL UnhookWindowsHookEx( HHOOK hhk)即可。

值得注意的是线程钩子和系统钩子的钩子函数的位置有很大的差别。线程钩子一般在当前线程或者当前线程派生的线程内,而系统钩子必须放在独立的动态链接库中,实现起来要麻烦一些。

0x04 一个实例——低级鼠标钩子程序

由0x02节,鼠标钩子类型有两个,一个是WH_MOUSE,另一个是WH_MOUSE_LL。其中WH_MOUSE_LL Hook,WH_MOUSE_LL Hook监视输入到线程消息队列中的鼠标消息。本例测试的是WH_MOUSE_LL。

参看上一小结可知,编写钩子程序的三个步奏是:

1.定义钩子函数:

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

1.安装钩子:

HHOOK SetWindowsHookEx( int idHook,HOOKPROC lpfn, INSTANCE hMod,DWORD dwThreadId )

1.卸载钩子:

BOOL UnhookWindowsHookEx( HHOOK hhk)

还需要注意一点:系统钩子必须放在独立的动态链接库中。由此,程序分为两个部分:一个是钩子程序动态链接库,实现了鼠标钩子程序;另一个是MFC操作窗体,对DLL进行加载和卸载,即对DLL进行测试。

1.HOOK DLL的编写

新建项目——> Visual C++ ——> MFC DLL

自定义消息

#define WM_HOOKMSG WM_USER + 106    // 自定义消息

共享代码段,所有线程共享

#pragma data_seg("SHARED")
static HHOOK hhkMouse = NULL; // 鼠标钩子句柄
static HINSTANCE g_hInstance = NULL; // 本DLL的实例句柄
static HWND g_hWnd = NULL; // 调用DLL的主窗口句柄
#pragma data_seg()
#pragma comment(linker,"/section:SHARED,rws")

定义低级鼠标子函数

LRESULT CALLBACK LowLevelMouseProc(int nCode,WPARAM wParam,LPARAM lParam)
{
// 有鼠标消息时,将其发给主程序
if ( g_hWnd != NULL && nCode == HC_ACTION)
{
::SendMessage(g_hWnd,WM_HOOKMSG,wParam,lParam);
}
return CallNextHookEx(hhkMouse,nCode,wParam,lParam);
}

安装低级鼠标子函数,从而截获系统所有的鼠标消息

#pragma data_seg("SHARED")
static HHOOK hhkMouse = NULL; // 鼠标钩子句柄
static HINSTANCE g_hInstance = NULL; // 本DLL的实例句柄
static HWND g_hWnd = NULL; // 调用DLL的主窗口句柄
#pragma data_seg()
#pragma comment(linker,"/section:SHARED,rws")

安装低级鼠标子函数,从而截获系统所有的鼠标消息

BOOL WINAPI StartHookMouse(HWND hwnd)
{
g_hWnd = hwnd;
hhkMouse = SetWindowsHookEx(WH_MOUSE_LL,LowLevelMouseProc,g_hInstance,0);
if ( NULL == hhkMouse)
{
return FALSE;
}
else
{
return TRUE;
}

}

卸载低级鼠标钩子

VOID WINAPI StopHookMouse()
{
if (hhkMouse != NULL)
{
::UnhookWindowsHookEx(hhkMouse);
}
}

获取自身的DLL句柄

// ChookDllApp 初始化
BOOL ChookDllApp::InitInstance()
{
CWinApp::InitInstance();

//
// 获取自身的dll句柄
//
g_hInstance = ::AfxGetInstanceHandle();

return TRUE;
}

hookDll.def文件,钩子的加载和卸载函数

; hookDll.def : 声明 DLL 的模块参数。
LIBRARY "MouseHook"

EXPORTS
; 此处可以是显式导出
StartHookMouse
StopHookMouse

MFC窗体编写

消息定义

#define WM_MOUSEMSG WM_USER + 106

添加消息映射

BEGIN_MESSAGE_MAP(ChookWindowDlg, CDialogEx)

//…… ……

ON_MESSAGE(WM_MOUSEMSG,&ChookWindowDlg::OnMouseMsg) //消息映射

END_MESSAGE_MAP()

鼠标钩子的加载和卸载

HINSTANCE g_hInstanceDll = NULL;
//
// 启动鼠标钩子
//
void ChookWindowDlg::OnBnClickedButtonStart()
{
// TODO: 在此添加控件通知处理程序代码
g_hInstanceDll = LoadLibrary(_T("hookDll.dll"));
if (NULL == g_hInstanceDll)
{
AfxMessageBox(_T("加载hookDll.dll失败"));
return;
}
typedef BOOL (CALLBACK *StartHookMouse)(HWND hwnd);
StartHookMouse startHook;
startHook = (StartHookMouse) ::GetProcAddress(g_hInstanceDll,"StartHookMouse");
if ( NULL == startHook )
{
AfxMessageBox(_T("获取 StartHookMouse 函数失败"));
return;
}

if (startHook(this->m_hWnd))
{
m_List.InsertItem(m_List.GetItemCount(),_T("0"));
m_List.SetItemText(m_List.GetItemCount()-1,1,_T("0"));
m_List.SetItemText(m_List.GetItemCount()-1,2,_T("启动鼠标钩子成功"));
}
else
{
m_List.InsertItem(m_List.GetItemCount(),_T("0"));
m_List.SetItemText(m_List.GetItemCount()-1,1,_T("0"));
m_List.SetItemText(m_List.GetItemCount()-1,2,_T("启动鼠标钩子失败"));
}

}

//
// 停止鼠标钩子HOOK
//
void ChookWindowDlg::OnBnClickedButtonHook()
{
// TODO: 在此添加控件通知处理程序代码
typedef VOID (CALLBACK *StopHookMouse)();
StopHookMouse stopHook;
g_hInstanceDll = LoadLibrary(_T("hookDll.dll"));
if ( g_hInstanceDll == NULL)
{
AfxMessageBox(_T("加载DLL失败"));
return;
}

stopHook = (StopHookMouse) ::GetProcAddress(g_hInstanceDll,"StopHookMouse");
if (stopHook == NULL)
{
m_List.InsertItem(m_List.GetItemCount(),_T("0"));
m_List.SetItemText(m_List.GetItemCount()-1,1,_T("0"));
m_List.SetItemText(m_List.GetItemCount()-1,2,_T("获取函数 StopHookMouse 失败"));
return;
}
else
{
stopHook();
m_List.InsertItem(m_List.GetItemCount(),_T("0"));
m_List.SetItemText(m_List.GetItemCount()-1,1,_T("0"));
m_List.SetItemText(m_List.GetItemCount()-1,2,_T("停止HOOKMOUSE成功"));
}

if (g_hInstanceDll != NULL)
{
::FreeLibrary(g_hInstanceDll);
}

// 确保list control 最后一行可见
m_List.EnsureVisible(m_List.GetItemCount()-1,FALSE);

}

   
3401 次浏览       16
 
相关文章

手机软件测试用例设计实践
手机客户端UI测试分析
iPhone消息推送机制实现与探讨
Android手机开发(一)
 
相关文档

Android_UI官方设计教程
手机开发平台介绍
android拍照及上传功能
Android讲义智能手机开发
相关课程

Android高级移动应用程序
Android系统开发
Android应用开发
手机软件测试
最新课程计划
信息架构建模(基于UML+EA)3-21[北京]
软件架构设计师 3-21[北京]
图数据库与知识图谱 3-25[北京]
业务架构设计 4-11[北京]
SysML和EA系统设计与建模 4-22[北京]
DoDAF规范、模型与实例 5-23[北京]

android人机界面指南
Android手机开发(一)
Android手机开发(二)
Android手机开发(三)
Android手机开发(四)
iPhone消息推送机制实现探讨
手机软件测试用例设计实践
手机客户端UI测试分析
手机软件自动化测试研究报告
更多...   

Android高级移动应用程序
Android应用开发
Android系统开发
手机软件测试
嵌入式软件测试
Android软、硬、云整合

领先IT公司 android开发平台最佳实践
北京 Android开发技术进阶
某新能源领域企业 Android开发技术
某航天公司 Android、IOS应用软件开发
阿尔卡特 Linux内核驱动
艾默生 嵌入式软件架构设计
西门子 嵌入式架构设计
更多...