windows下钩子的使用

钩子使用指南     
    
  钩子的概念   
    钩子机制允许应用程序截获处理window消息或特定事件。与DOS中断截获处理机制有类似之处。钩子(Hook),是Windows消息处理机制的一个平台(point),应用程序可以在上面设置子程以监视指定窗口的某种消息,而且所监视的窗口可以是其他进程所创建的。当消息到达后,在目标窗口处理函数之前处理它。每一个hook都有一个与之相关联的指针列表,称之为钩子链表,该链表中的指针指向这个钩子的各个处理子程。一个钩子处理一种类型的消息。当钩子所监视的消息出现时,Windows调用链表中的第一个钩子子程。某些类型的钩子只能在系统范围内设置处理子程,其余类型的钩子还可以在特定的线程中设置(用SetWindowsHookEx设置)。对于某一特定类型的钩子(它处理某一类型的消息),线程钩子子程被先调用,接着是系统钩子子程。钩子子程是一个应用程序定义的回调函数(callback   function),不能定义成某个类的成员函数,只能定义为普通的C函数。例如:   
    LRESULT   CALLBACK   HookProc(int   nCode   ,WPARAM   wParam,LPARAM   lParam)   
    
  钩子的设置   
    可以使用API函数SetWindowsHookEx()把一个应用程序定义的钩子子程安装到钩子链表中。所安装的钩子子程用以监视系统或某一特定类型的事件,这些事件可以是与某一特定线程关联的,也可以是系统中所有线程的事件。   
    HHOOK   SetWindowsHookEx(     
       int   idHook,   //   钩子的类型,即它处理的消息类型   
       HOOKPROC   lpfn,   //   钩子子程的地址指针。如果dwThreadId参数为0     
               或是一个由别的进程创建的线程的标识,lpfn必   
               须指向DLL中的钩子子程。除此以外,lpfn可以   
               指向当前进程的一段钩子子程代码。     
       HINSTANCE   hMod,   //   应用程序实例的句柄。标识包含lpfn所指的子程   
               的DLL。如果dwThreadId   标识当前进程创建的一   
               个线程,而且子程代码位于当前进程,hMod必须   
               为NULL。     
       DWORD   dwThreadId   //   与安装的钩子子程相关联的线程的标识符,   
               如果为0,钩子子程与所有的线程关联。   
       );     
    函数成功则返回钩子子程的句柄,失败返回NULL。   
    以上所说的钩子子程与线程相关联是指在一钩子链表中发给该线程的消息同时发送给钩子子程,且被钩子子程先处理。   
    虽然这并不是强制的,但一般推荐在钩子子程中调用CallNextHookEx()函数以执行钩子链表所指的下一个钩子子程。否则安装了别的钩子的应用程序就会收不到钩子通知,从而产生错误的结果。除非确实需要禁止向别的应用程序发通知,否则应调用CallNextHookEx()。程序终止之前,必须调用UnhookWindowsHookEx()函数释放钩子关联的系统资源。   
    
  钩子的实例   
    //找到要截获其消息的窗口的句柄   
    HWND   hWnd=::FindWindow(   
                "#32770",//LPCTSTR   lpClassName,     
                NULL   //LPCTSTR   lpWindowName     
                );     
    if(hWnd)   //如果窗口句柄非零,则设置钩子,   
      HHOOK   oldkeyhook=SetWindowsHookEx(   
               WH_KEYBOARD,   //截获键盘消息的钩子类型   
               (HOOKPROC)KeyBoardProc,   //钩子子程地址   
               //包含钩子子程的动态链接库的地址   
               GetModuleHandle("hookdll.dll"),     
               //得到创建该窗口的线程的标识   
               GetWindowThreadProcessId(hWnd,NULL));   
    
    释放钩子函数的资源:   
            UnhookWindowsHookEx(oldkeyhook);   
    
  钩子消息处理函数:   
    建议在动态链接库中实现钩子消息处理函数,这样比较不容易犯错误。   
    下面是钩子消息处理函数的例子:   
    LRESULT   CALLBACK   KeyBoardProc(int   nCode,   WPARAM   wParam,   LPARAM   lParam)   
    {   
      //当接收到的消息为WM_KEYUPSH时,存储字符到文件log.sys中   
      if(lParam&0x80000000)   
      {   
        char   code=(char)wParam;   
    CFile     
    file("c:\\log.sys",CFile::modeCreate|CFile::modeNoTruncate|CFile::modeWrite);   
         file.SeekToEnd();   
         file.Write(&code,1);   
         file.Close();   
      }     
      //调用CallNextHookEx()函数,并返回该函数的返回值。   
      return   CallNextHookEx(oldkeyhook,   nCode,   wParam,   lParam);   
    }
二、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、付费专栏及课程。

余额充值