HOOK 钩子编程

所谓钩子函数就是对windows下的如(键盘或鼠标)等消息的过滤处理,通过钩子回调函数来进行截留处理。

 

 

 

 

 

 

 

 

 

一共有两种类型的 钩子:局部的和远程的。
局部 钩子仅钩挂您自己进程的事件。
远程的 钩子还可以将钩挂其它进程发生的事件。
远程的 钩子又有两种:
钩子分两种, 一种是系统级的全局钩子; 一种是线程级的钩子.全局钩子函数需要定义在 DLL 中, 线程级的钩子开始比较简单.
其实钩子函数就三个:
设置钩子: SetWindowsHookEx
释放钩子: UnhookWindowsHookEx
继续钩子: CallNextHookEx
在线程级的钩子中经常用到 GetCurrentThreadID 函数来获取当前线程的 ID
 
 
SetWindowsHookEx( __in int idHook, __in HOOKPROC lpfn, __in_opt HINSTANCE hmod, __in DWORD dwThreadId);
idHook是钩子的类型
类型如下:
lpfn是钩子回调函数,也是过滤函数。
 
 
当为局部钩子时,第三个参数一般为null,第四个线程idGetCurrentThreadId()
当为全局钩子时,第三个参数为dll实例句柄,第四个参数为0
 
 
 
局部钩子实例如下:取消键盘退出键消息
WPARAM为产生的虚拟键值
g_hook = SetWindowsHookEx( WH_KEYBOARD, hookproc, NULL, GetCurrentThreadId() )
LRESULT CALLBACK hookproc(int code, WPARAM wParam, LPARAM lParam)
{
 if( wParam == VK_ESCAPE )
 return 1;
 else
 {
  return
   CallNextHookEx( g_hook, code, wParam, lParam );
 }
 
CallNextHookEx( g_hook, code, wParam, lParam );为调用下一个钩子函数
 
全局钩子:需要创建动态库dll,实例如下   取消键盘消息

HHOOK g_hook = NULL;
LRESULT CALLBACK hookdl( int code, WPARAM wparm, LPARAM lparm );
HOOK_API void  SetHook()
{
   g_hook = SetWindowsHookEx( WH_KEYBOARD, hookdl, GetModuleHandle("hook") , 0);
}
LRESULT CALLBACK hookdl( int code, WPARAM wparm, LPARAM lparm )
{
 return 1;


}

 
 
UnhookWindowsHookEx() 为移除钩子句柄,从钩子连中移走一个已安装的钩子
当您创建一个 钩子时,WINDOWS会先在内存中创建一个 数据结构,该数据结构包含了钩子的相关信息,然后把该 结构体加到已经存在的钩子链表中去。新的 钩子将加到老的前面。当一个事件发生时,如果您安装的是一个局部 钩子,您进程中的钩子函数将被调用。如果是一个远程 钩子,系统就必须把钩子函数插入到其它进程的 地址空间,要做到这一点要求钩子函数必须在一个 动态链接库中,所以如果您想要使用远程钩子,就必须把该钩子函数放到动态链接库中去。

 

二、API Hook的原理 这里的API既包括传统的Win32 APIs,也包括任何Module输出的函数调用。熟悉PE文件格 式的朋友都知道,PE文件将对外部Module输出函数的调用信息保存在输入表中,即.idata段。 下面首先介绍本段的结构。 输入表首先以一个IMAGE_IMPORT_DESCRIPTOR(简称IID)数组开始。每个被PE文件隐式链接 进来的DLL都有一个IID.在这个数组中的最后一个单元是NULL,可以由此计算出该数组的项数。 例如,某个PE文件从两个DLL中引入函数,就存在两个IID结构来描述这些DLL文件,并在两个 IID结构的最后由一个内容全为0的IID结构作为结束。几个结构定义如下: IMAGE_IMPORT_DESCRIPTOR struct union{ DWORD Characteristics; ;00h DWORD OriginalFirstThunk; }; TimeDateStamp DWORD ;04h ForwarderChain DWORD ;08h Name DWORD ;0Ch FirstThunk DWORD ;10h IMAGE_IMPROT_DESCRIPTOR ends typedef struct _IMAGE_THUNK_DATA{ union{ PBYTE ForwarderString; PDWORD Functions; DWORD Ordinal; PIMAGE_IMPORT_BY_NAME AddressOfData; }u1; } IMAGE_IMPORT_BY_NAME结构保存一个输入函数的相关信息: IMAGE_IMPORT_BY_NAME struct Hint WORD ? ;本函数在其所驻留DLL的输出表中的序号 Name BYTE ? ;输入函数的函数名,以NULL结尾的ASCII字符串 IMAGE_IMPORT_BY_NAME ends OriginalFirstThunk(Characteristics):这是一个IMAGE_THUNK_DATA数组的RVA(相对于PE文件 起始处)。其中每个指针都指向IMAGE_IMPORT_BY_NAME结构。 TimeDateStamp:一个32位的时间标志,可以忽略。 ForwarderChain:正向链接索引,一般为0。当程序引用一个DLL中的API,而这个API又引用别的 DLL的API时使用。 NameLL名字的指针。是个以00结尾的ASCII字符的RVA地址,如"KERNEL32.DLL"。 FirstThunk:通常也是一个IMAGE_THUNK_DATA数组的RVA。如果不是一个指针,它就是该功能在 DLL中的序号。 OriginalFirstThunk与FirstThunk指向两个本质相同的数组IMAGE_THUNK_DATA,但名称不同, 分别是输入名称表(Import Name Table,INT)和输入地址表(Import Address Table,IAT)。 IMAGE_THUNK_DATA结构是个双字,在不同时刻有不同的含义,当双字最高位为1时,表示函数以 序号输入,低位就是函数序号。当双字最高位为0时,表示函数以字符串类型的函数名 方式输入,这时它是指向IMAGE_IMPORT_BY_NAME结构的RVA。 三个结构关系如下图: IMAGE_IMPORT_DESCRIPTOR INT IMAGE_IMPORT_BY_NAME IAT -------------------- /-->---------------- ---------- ---------------- |01| 函数1 ||02| 函数2 || n| ... |"USER32.dll" | |--------------------| | | FirstThunk |---------------------------------------------------------------/ -------------------- 在PE文件中对DLL输出函数的调用,主要以这种形式出现: call dword ptr[xxxxxxxx] 或 jmp [xxxxxxxx] 其中地址xxxxxxxx就是IAT中一个IMAGE_THUNK_DATA结构的地址,[xxxxxxxx]取值为IMAGE_THUNK_DATA 的值,即IMAGE_IMPORT_BY_NAME的地址。在操作系统加载PE文件的过程中,通过IID中的Name加载相应 的DLL,然后根据INT或IAT所指向的IMAGE_IMPORT_BY_NAME中的输入函数信息,在DLL中确定函数地址, 然后将函数地址写到IAT中,此时IAT将不再指向IMAGE_IMPORT_BY_NAME数组。这样[xxxxxxxx]取到的 就是真正的API地址。 从以上分析可以看出,要拦截API的调用,可以通过改写IAT来实现,将自己函数的地址写到IAT中, 达到拦截目的。 另外一种方法的原理更简单,也更直接。我们不是要拦截吗,先在内存中定位要拦截的API的地址, 然后改写代码的前几个字节为 jmp xxxxxxxx,其中xxxxxxxx为我们的API的地址。这样对欲拦截API的 调用实际上就跳转到了咱们的API调用去了,完成了拦截。不拦截时,再改写回来就是了。 这都是自己从网上辛辛苦苦找来的,真的很好啊
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值