一、消息钩子的概念
1、基本概念
Windows应用程序是基于消息驱动的,任何线程只要注册窗口类都会有一个消息队列用于接收用户输入的消息和系统消息。为了拦截消息,Windows提出了钩子的概念。钩子(Hook)是Windows消息处理机制中的一个监视点,钩子提供一个回调函数。当在某个程序中安装钩子后,它将监视该程序的消息,在指定消息还没到达窗口之前钩子程序先捕获这个消息。这样就有机会对此消息进行过滤,或者对Windows消息实现监控。
2、分类
消息钩子分为局部钩子和全局钩子。局部钩子是指仅拦截指定一个进程的指定消息,全局钩子将拦截系统中所有进程的指定消息。
3、实现步骤
使用钩子技术拦截消息通常分为如下几个步骤:
●
设置钩子回调函数;(拦截到消息后所调用的函数)
安装钩子;(使用SetWindowsHookEx函数)
卸载钩子。(使用UnhookWindowsHookEx函数)
4、功能
利用消息钩子可以实现特效界面、同步消息、监控消息、自启动等功效。
二、病毒对消息钩子技术的利用
计算机病毒经常利用消息钩子实现两种功能:
1、监控用户按键,盗取用户信息。
这样的病毒会启动一个常驻内存的EXE病毒进程,然后安装一个全局键盘消息钩子,钩子回调函数位于病毒进程中,这样系统中任何有按键操作的进程,其按键详细信息都会被病毒进程拦截记录。
2、自启动
这样的病毒会将钩子回调函数放在一个DLL文件中,然后安装一个全局消息(容易触发的消息,如WH_CBT、WH_GETMESSAGE等)钩子,这样凡响应该消息的进程都会自动加载病毒的DLL,病毒也就跟着自动运行了。
三、消息钩子病毒的对抗技术(重点)
1、对抗技术原理
对付消息钩子病毒方法很简单,只要将病毒安装的钩子卸载掉即可。(注意:对于系统中许多进程已经因为全局钩子而加载了病毒DLL的情况,并不需要去卸载这些DLL,只要安装的消息钩子被卸载那么对应的DLL也都会被在这些进程中自动卸载。)卸载钩子有两种方法:
(1)、结束掉安装钩子的进程
将设置钩子的进程结束,进程在退出之前会自行卸载掉该进程安装的所有消息钩子。这种方法很适合对付监控用户按键的病毒。
(2)、获得消息钩子句柄,然后调用UnhookWindowsHookEx函数即可将消息钩子卸载。
如果病毒单独启动了一个病毒进程安装了一个全局消息钩子,然后就常驻内存。这时我们将这个病毒进程结束掉即可。但是如果病毒在系统进程中注入代码而安装的钩子,这样钩子句柄就位于系统进程中,我们不可以结束系统进程,这时就只能获取这个消息钩子句柄,然后调用函数卸载。
2、对抗技术实现细节
对于结束掉安装钩子进程从而卸载病毒消息钩子的方法很容易实现,只要找到病毒进程结束即可。而对于获取病毒消息钩子句柄,然后调用函数卸载钩子的方法比较复杂,也是本文重点讨论的内容,将在下一个标题中详细介绍。
四、查找病毒消息钩子句柄然后卸载的方法实现(重点、难点)
1、实现原理分析
系统会将所有安装的钩子句柄保存在内核中,要查找病毒安装的消息钩子句柄,我们要枚举所有的消息钩子句柄。如何枚举稍后讲解,还要解决一个问题,就是在枚举过程中,我们怎么知道哪个句柄是病毒安装的呢?
通过分析病毒样本我们通常可以得到病毒安装钩子就是为了令其他合法进程加载病毒DLL,所以它会将钩子回调函数写在该DLL中。在枚举消息钩子句柄时,同时也可以得到该句柄所对应的回调函数所属的DLL模块,根据这个DLL模块是不是病毒的DLL模块即可找到病毒的消息钩子句柄,最后将其卸载即可。
关于如何枚举系统消息钩子句柄,对于不同的操作系统方法大不相同,这里介绍一种用户层读内存的方法,此方法仅在2000/XP系统下可用。
在2000/XP系统下有一个Windows用户界面相关的应用程序接口User32.dll。它用于包括Windows窗口处理,基本用户界面等特性,如创建窗口和发送消息。当它被加载到内存后,它保存了所有Windows窗口、消息相关的句柄,其中就包括消息钩子句柄。这些句柄被保存在一块共享内存段中,通常称为R3层的GUI TABLE。所以只要我们找到GUI TABLE,然后在其中的句柄中筛选出消息钩子句柄。GUI TABLE这块内存段可以被所有进程空间访问。GUI TABLE被定义成如下结构:
typedef struct tagSHAREDINFO {
struct tagSERVERINFO *pServerInfo; //指向tagSERVERINFO结构的指针
struct _HANDLEENTRY *pHandleEntry; // 指向句柄表
struct tagDISPLAYINFO *pDispInfo; //指向tagDISPLAYINFO结构的指针
ULONG ulSharedDelta;
LPWSTR pszDllList;
} SHAREDINFO, *PSHAREDINFO;
tagSHAREDINFO结构体的第一个成员pServerInfo所指向的tagSERVERINFO结构体定义如下。
typedef struct tagSERVERINFO {
short wRIPFlags ;
short wSRVIFlags ;
short wRIPPID ;
short wRIPError ;
ULONG cHandleEntries; //句柄表中句柄的个数
}SERVERINFO,*PSERVERINFO;
可以看出通过tagSERVERINFO结构的cHandleEntries成员即可得到tagSHAREDINFO结构的pHandleEntry成员所指向的句柄表中的句柄数。
tagSHAREDINFO结构体的第二个成员pHandleEntry是指向_HANDLEENTRY结构体数组起始地址的指针,该数组的一个成员对应一个句柄。句柄结构体_HANDLEENTRY定义如下。
typedef struct _HANDLEENTRY{
PVOID pObject; //指向句柄所对应的内核对象
ULONG pOwner;
BYTE bType; //句柄的类型
BYTE bFlags;
short wUniq;
}HANDLEENTRY,*PHANDLEENTRY;
_HANDLEENTRY结构体成员bType是句柄的类型,通过该变量的判断可以筛选消息钩子句柄。User32中保存的句柄类型通常有如下种类。
typedef enum _HANDLE_TYPE
{
TYPE_FREE = 0,
TYPE_WINDOW = 1 ,
TYPE_MENU = 2, //菜单句柄
TYPE_CURSOR = 3, //光标句柄
TYPE_SETWINDOWPOS = 4,
TYPE_HOOK = 5, //消息钩子句柄
TYPE_CLIPDATA = 6 ,
TYPE_CALLPROC = 7,
TYPE_ACCELTABLE = 8,
TYPE_DDEACCESS = 9,
TYPE_DDECONV = 10,
TYPE_DDEXACT = 11,
TYPE_MONITOR = 12,
TYPE_KBDLAYOUT = 13 ,
TYPE_KBDFILE = 14 ,
TYPE_WINEVENTHOOK = 15 ,
TYPE_TIMER = 16,
TYPE_INPUTCONTEXT = 17 ,
TYPE_CTYPES = 18 ,
TYPE_GENERIC = 255
}HANDLE_TYPE;
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课