如何拦截系统底层API文件操作请大虾解答

解决方案 »

  1.   

    来源:http://cisco.chinaitlab.com/safety/366596.html详谈HOOK API的技术
    HOOK API是一个永恒的话题,如果没有HOOK,许多技术将很难实现,也许根本不能实现。这里所说的API,是广义上的API,它包括DOS下的中断,WINDOWS里的API、中断服务、IFS和NDIS过滤等。比如大家熟悉的即时翻译软件,就是靠HOOK TextOut()或ExtTextOut()这两个函数实现的,在操作系统用这两个函数输出文本之前,就把相应的英文替换成中文而达到即时翻译;IFS 和NDIS过滤也是如此,在读写磁盘和收发数据之前,系统会调用第三方提供的回调函数来判断操作是否可以放行,它与普通HOOK不同,它是操作系统允许的,由操作系统提供接口来安装回调函数。    甚至如果没有HOOK,就没有病毒,因为不管是DOS下的病毒或WINDOWS里的病毒,都是靠HOOK系统服务来实现自己的功能的:DOS下的病毒靠 HOOK INT 21来感染文件(文件型病毒),靠HOOK INT 13来感染引导扇区(引导型病毒);WINDOWS下的病毒靠HOOK系统API(包括RING0层的和RING3层的),或者安装IFS(CIH病毒所用的方法)来感染文件。因此可以说“没有HOOK,就没有今天多姿多彩的软件世界”。    由于涉及到专利和知识产权,或者是商业机密,微软一直不提倡大家HOOK它的系统API,提供IFS和NDIS等其他过滤接口,也是为了适应杀毒软件和防火墙的需要才开放的。所以在大多数时候,HOOK API要靠自己的力量来完成。    HOOK API有一个原则,这个原则就是:被HOOK的API的原有功能不能受到任何影响。就象医生救人,如果把病人身体里的病毒杀死了,病人也死了,那么这个“ 救人”就没有任何意义了。如果你HOOK API之后,你的目的达到了,但API的原有功能失效了,这样不是HOOK,而是REPLACE,操作系统的正常功能就会受到影响,甚至会崩溃。    HOOK API的技术,说起来也不复杂,就是改变程序流程的技术。在CPU的指令里,有几条指令可以改变程序的流程:JMP,CALL,INT,RET,RETF,IRET等指令。理论上只要改变API入口和出口的任何机器码,都可以HOOK,但是实际实现起来要复杂很多,因为要处理好以下问题:    1,CPU指令长度问题,在32位系统里,一条JMP/CALL指令的长度是5个字节,因此你只有替换API里超过5个字节长度的机器码(或者替换几条指令长度加起来是5字节的指令),否则会影响被更改的小于5个字节的机器码后面的数条指令,甚至程序流程会被打乱,产生不可预料的后果;    2,参数问题,为了访问原API的参数,你要通过EBP或ESP来引用参数,因此你要非常清楚你的HOOK代码里此时的EBP/ESP的值是多少;    3,时机的问题,有些HOOK必须在API的开头,有些必须在API的尾部,比如HOOK CreateFilaA(),如果你在API尾部HOOK API,那么此时你就不能写文件,甚至不能访问文件;HOOK RECV(),如果你在API头HOOK,此时还没有收到数据,你就去查看RECV()的接收缓冲区,里面当然没有你想要的数据,必须等RECV()正常执行后,在RECV()的尾部HOOK,此时去查看RECV()的缓冲区,里面才有想要的数据;    4,上下文的问题,有些HOOK代码不能执行某些操作,否则会破坏原API的上下文,原API就失效了;    5,同步问题,在HOOK代码里尽量不使用全局变量,而使用局部变量,这样也是模块化程序的需要;    6,最后要注意的是,被替换的CPU指令的原有功能一定要在HOOK代码的某个地方模拟实现。    下面以ws2_32.dll里的send()为例子来说明如何HOOK这个函数:    Exported fn(): send - Ord:0013h    地址     机器码             汇编代码    :71A21AF4 55               push ebp //将被HOOK的机器码(第1种方法)    :71A21AF5 8BEC             mov ebp, esp //将被HOOK的机器码(第2种方法)    :71A21AF7 83EC10             sub esp, 00000010    :71A21AFA 56               push esi    :71A21AFB 57               push edi    :71A21AFC 33FF             xor edi, edi    :71A21AFE 813D1C20A371931CA271   cmp dword ptr [71A3201C], 71A21C93 //将被HOOK的机器码(第4种方法)    :71A21B08 0F84853D0000         je 71A25893    :71A21B0E 8D45F8             lea eax, dword ptr [ebp-08]    :71A21B11 50               push eax    :71A21B12 E869F7FFFF         call 71A21280    :71A21B17 3BC7             cmp eax, edi    :71A21B19 8945FC             mov dword ptr [ebp-04], eax    :71A21B1C 0F85C4940000         jne 71A2AFE6    :71A21B22 FF7508             push [ebp+08]    :71A21B25 E826F7FFFF         call 71A21250    :71A21B2A 8BF0             mov esi, eax    :71A21B2C 3BF7             cmp esi, edi    :71A21B2E 0F84AB940000         je 71A2AFDF    :71A21B34 8B4510             mov eax, dword ptr [ebp+10]    :71A21B37 53               push ebx    :71A21B38 8D4DFC             lea ecx, dword ptr [ebp-04]    :71A21B3B 51               push ecx    :71A21B3C FF75F8             push [ebp-08]    :71A21B3F 8D4D08             lea ecx, dword ptr [ebp+08]    :71A21B42 57               push edi    :71A21B43 57               push edi    :71A21B44 FF7514             push [ebp+14]    :71A21B47 8945F0             mov dword ptr [ebp-10], eax    :71A21B4A 8B450C             mov eax, dword ptr [ebp+0C]    :71A21B4D 51               push ecx    :71A21B4E 6A01             push 00000001    :71A21B50 8D4DF0             lea ecx, dword ptr [ebp-10]    :71A21B53 51               push ecx    :71A21B54 FF7508             push [ebp+08]    :71A21B57 8945F4             mov dword ptr [ebp-0C], eax    :71A21B5A 8B460C             mov eax, dword ptr [esi+0C]    :71A21B5D FF5064             call [eax+64]    :71A21B60 8BCE             mov ecx, esi    :71A21B62 8BD8             mov ebx, eax    :71A21B64 E8C7F6FFFF         call 71A21230 //将被HOOK的机器码(第3种方法)    :71A21B69 3BDF             cmp ebx, edi    :71A21B6B 5B               pop ebx    :71A21B6C 0F855F940000         jne 71A2AFD1    :71A21B72 8B4508             mov eax, dword ptr [ebp+08]    :71A21B75 5F               pop edi    :71A21B76 5E               pop esi    :71A21B77 C9               leave    :71A21B78 C21000             ret 0010    下面用4种方法来HOOK这个API:    1,把API入口的第一条指令是PUSH EBP指令(机器码0x55)替换成INT 3(机器码0xcc),然后用WINDOWS提供的调试函数来执行自己的代码,这中方法被SOFT ICE等DEBUGER广泛采用,它就是通过BPX在相应的地方设一条INT 3指令来下断点的。但是不提倡用这种方法,因为它会与WINDOWS或调试工具产生冲突,而汇编代码基本都要调试;    2,把第二条mov ebp,esp指令(机器码8BEC,2字节)替换为INT F0指令(机器码CDF0),然后在IDT里设置一个中断门,指向我们的代码。我这里给出一个HOOK代码:    lea ebp,[esp+12] //模拟原指令mov ebp,esp的功能    pushfd         //保存现场    pushad         //保存现场    //在这里做你想做的事情    popad         //恢复现场    popfd         //恢复现场    iretd         //返回原指令的下一条指令继续执行原函数(71A21AF7地址处)    这种方法很好,但缺点是要在IDT设置一个中断门,也就是要进RING0.    3,更改CALL指令的相对地址(CALL分别在71A21B12、71A21B25、71A21B64,但前面2条CALL之前有一个条件跳转指令,有可能不被执行到,因此我们要HOOK 71A21B64处的CALL指令)。为什么要找CALL指令下手?因为它们都是5字节的指令,而且都是CALL指令,只要保持操作码0xE8不变,改变后面的相对地址就可以转到我们的HOOK代码去执行了,在我们的HOOK代码后面再转到目标地址去执行。    假设我们的HOOK代码在71A20400处,那么我们把71A21B64处的CALL指令改为CALL 71A20400(原指令是这样的:CALL 71A21230)    而71A20400处的HOOK代码是这样的:    71A20400:    pushad    //在这里做你想做的事情    popad    jmp 71A21230   //跳转到原CALL指令的目标地址,原指令是这样的:call 71A21230    这种方法隐蔽性很好,但是比较难找这条5字节的CALL指令,计算相对地址也复杂。    4,替换71A21AFE地址上的cmp dword ptr [71A3201C], 71A21C93指令(机器码:813D1C20A371931CA271,10字节)成为 call 71A20400    nop    nop    nop    nop    nop    (机器码:E8 XX XX XX XX 90 90 90 90 90,10字节)    在71A20400的HOOK代码是: pushad    mov edx,71A3201Ch           //模拟原指令cmp dword ptr [71A3201C], 71A21C93    cmp dword ptr [edx],71A21C93h   //模拟原指令cmp dword ptr [71A3201C], 71A21C93    pushfd //在这里做你想做的事    popfd    popad    ret    这种方法隐蔽性最好,但不是每个API都有这样的指令,要具体情况具体操作。    以上几种方法是常用的方法,值得一提的是很多人都是改API开头的5个字节,但是现在很多杀毒软件用这样的方法检查API是否被HOOK,或其他病毒木马在你之后又改了前5个字节,这样就会互相覆盖,最后一个HOOK API的操作才是有效的,所以提倡用第3和第4种方法。
    --------------------------------
      

  2.   

    来源: http://yunxintao.bloghome.cn/posts/128181.htmlWindows下Hook API技术小结
    1、基本概念  钩子(Hook),是Windows消息处理机制的一个平台,应用程序可以在上面设置子程以监视指定窗口的某种消息,而且所监视的窗口可以是其他进程所创建的。当消息到达后,在目标窗口处理函数之前处理它。钩子机制允许应用程序截获处理window消息或特定事件。钩子实际上是一个处理消息的程序段,通过系统调用,把它挂入系统。每当特定的消息发出,在没有到达目的窗口前,钩子程序就先捕获该消息,亦即钩子函数先得到控制权。这时钩子函数即可以加工处理(改变)该消息,也可以不作处理而继续传递该消息,还可以强制结束消息的传递。Hook API是指Windows开放给程序员的编程接口,使得在用户级别下可以对操作系统进行控制,也就是一般的应用程序都需要调用API来完成某些功能,Hook API的意思就是在这些应用程序调用真正的系统API前可以先被截获,从而进行一些处理再调用真正的API来完成功能。HOOK分为三种:LOCAL HOOK 和 REMOTE HOOK,还有一种是SYSTEM-WIDE LOCAL HOOK。LOCAL HOOK就是指程序HOOK的就是本程序中的线程。REMOTE HOOK有两种形式:一种是对其他程序中某个特定的线程;一种是对整个系统。SYSTEM–WIDE LOCAL HOOK 是一种比较特殊的。它具有REMOTE HOOK的功能,又可以用LOCAL HOOK 的表现手法,实际上就是WH_JOURNALRECORD和WH_JOURNALPLAYBACK两种HOOK。REMOTE HOOK 必须封装在DLL中。这是因为REMOTE HOOK是针对整个系统或其他进程的线程,因此HOOK必须封装成DLL,才可以植入到其他进程进行监控。而SYSTEM-WIDE LOCAL HOOK采用的是另外一种架构,系统中的线程请求或获得一个硬件消息的话,系统会调用那个安装有HOOK的线程,并执行它的FILTER FUNCTION.然后再返回给请求硬件消息的线程。这种架构有一个缺点就是如果HOOK FILTER FUNCTION的处理中进入无限循环的话,那么整个系统将停留再循环中,无法切换到其他线程。为了处理这个缺陷,WINDOW使用了一个办法来处理:就是CTRL+ESC键,如果用户按下CTRL+ESC键,则系统将会发送一个WM_CANCELJOUNAL消息到有挂上JOUNAL 系列HOOK的线程上面。 2、运行机制2.1、钩子链表和钩子子程每一个Hook都有一个与之相关联的指针列表,称之为钩子链表,由系统来维护。这个列表的指针指向指定的,应用程序定义的,被Hook子程调用的回调函数,也就是该钩子的各个处理子程。当与指定的Hook类型关联的消息发生时,系统就把这个消息传递到Hook子程。一些Hook子程可以只监视消息,或者修改消息,或者停止消息的前进,避免这些消息传递到下一个Hook子程或者目的窗口。最近安装的钩子放在链的开始,而最早安装的钩子放在最后,也就是后加入的先获得控制权。Windows 并不要求钩子子程的卸载顺序一定得和安装顺序相反。每当有一个钩子被卸载,Windows 便释放其占用的内存,并更新整个Hook链表。如果程序安装了钩子,但是在尚未卸载钩子之前就结束了,那么系统会自动为它做卸载钩子的操作。钩子子程是一个应用程序定义的回调函数(CALLBACK Function),不能定义成某个类的成员函数,只能定义为普通的C函数。用以监视系统或某一特定类型的事件,这些事件可以是与某一特定线程关联的,也可以是系统中所有线程的事件。钩子子程必须按照以下的语法:LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam);HookProc是应用程序定义的名字。nCode参数是Hook代码,Hook子程使用这个参数来确定任务。这个参数的值依赖于Hook类型,每一种Hook都有自己的Hook代码特征字符集。wParam和lParam参数的值依赖于Hook代码,但是它们的典型值是包含了关于发送或者接收消息的信息。
     2.2、钩子的安装与释放使用API函数SetWindowsHookEx()把一个应用程序定义的钩子子程安装到钩子链表中。SetWindowsHookEx函数总是在Hook链的开头安装Hook子程。当指定类型的Hook监视的事件发生时,系统就调用与这个Hook关联的Hook链的开头的Hook子程。每一个Hook链中的Hook子程都决定是否把这个事件传递到下一个Hook子程。Hook子程传递事件到下一个Hook子程需要调用CallNextHookEx函数。HHOOK SetWindowsHookEx(      
    int idHook,                   // 钩子的类型,即它处理的消息类型     
    HOOKPROC lpfn,      // 钩子子程的地址指针。如果dwThreadId参数为0                                                       
                        // 或是一个由别的进程创建的线程的标识,                                                         
                        // lpfn必须指向DLL中的钩子子程。                                                       
                        // 除此以外,lpfn可以指向当前进程的一段钩子子程代码。                                                         
                        // 钩子函数的入口地址,当钩子钩到任何消息后便调用这个函数。     
    HINSTANCE hMod,     // 应用程序实例的句柄。标识包含lpfn所指的子程的DLL                                                          
                        // 如果dwThreadId 标识当前进程创建的一个线程,                                                          
                        // 而且子程代码位于当前进程,hMod必须为NULL。                                                          
                        // 可以很简单的设定其为本应用程序的实例句柄。     
    DWORD dwThreadId    // 与安装的钩子子程相关联的线程的标识符。                                                            
                        // 如果为0,钩子子程与所有的线程关联,即为全局钩子。
    );   函数成功则返回钩子子程的句柄,失败返回NULL。  以上所说的钩子子程与线程相关联是指在一钩子链表中发给该线程的消息同时发送给钩子子程,且被钩子子程先处理。在钩子子程中调用得到控制权的钩子函数在完成对消息的处理后,如果想要该消息继续传递,那么它必须调用另外一个SDK中的API函数CallNextHookEx来传递它,以执行钩子链表所指的下一个钩子子程。这个函数成功时返回钩子链中下一个钩子过程的返回值,返回值的类型依赖于钩子的类型。这个函数的原型如下:LRESULT CallNextHookEx   (    HHOOK hhk;    int nCode;    WPARAM wParam;    LPARAM lParam;    );hhk为当前钩子的句柄,由SetWindowsHookEx()函数返回。NCode为传给钩子过程的事件代码。wParam和lParam 分别是传给钩子子程的wParam值,其具体含义与钩子类型有关。钩子函数也可以通过直接返回TRUE来丢弃该消息,并阻止该消息的传递。否则的话,其他安装了钩子的应用程序将不会接收到钩子的通知而且还有可能产生不正确的结果。钩子在使用完之后需要用UnHookWindowsHookEx()卸载,否则会造成麻烦。释放钩子比较简单,UnHookWindowsHookEx()只有一个参数。函数原型如下:UnHookWindowsHookEx( HHOOK hhk; );函数成功返回TRUE,否则返回FALSE。 2.3、一些运行机制:在Win16环境中,DLL的全局数据对每个载入它的进程来说都是相同的;而在Win32环境中,情况却发生了变化,DLL函数中的代码所创建的任何对象(包括变量)都归调用它的线程或进程所有。当进程在载入DLL时,操作系统自动把DLL地址映射到该进程的私有空间,也就是进程的虚拟地址空间,而且也复制该DLL的全局数据的一份拷贝到该进程空间。也就是说每个进程所拥有的相同的DLL的全局数据,它们的名称相同,但其值却并不一定是相同的,而且是互不干涉的。因此,在Win32环境下要想在多个进程中共享数据,就必须进行必要的设置。在访问同一个Dll的各进程之间共享存储器是通过存储器映射文件技术实现的。也可以把这些需要共享的数据分离出来,放置在一个独立的数据段里,并把该段的属性设置为共享。必须给这些变量赋初值,否则编译器会把没有赋初始值的变量放在一个叫未被初始化的数据段中。 
    #pragma data_seg  预处理指令用于设置共享数据段。例如:#pragma data_seg(“SharedDataName”) HHOOK hHook = NULL; #pragma data_seg()在 # pragma data_seg(“SharedDataName”) 和 #pragma data_seg() 之间的所有变量将被访问该Dll的所有进程看到和共享。再加上一条指令#pragma comment(linker,"/section:.SharedDataName,rws"),那么这个数据节中的数据可以在所有DLL的实例之间共享。所有对这些数据的操作都针对同一个实例的,而不是在每个进程的地址空间中都有一份。当进程隐式或显式调用一个动态库里的函数时,系统都要把这个动态库映射到这个进程的虚拟地址空间里(以下简称"地址空间")。这使得DLL成为进程的一部分,以这个进程的身份执行,使用这个进程的堆栈。
     2.4、系统钩子与线程钩子SetWindowsHookEx()函数的最后一个参数决定了此钩子是系统钩子还是线程钩子。线程勾子用于监视指定线程的事件消息。线程勾子一般在当前线程或者当前线程派生的线程内。系统勾子监视系统中的所有线程的事件消息。因为系统勾子会影响系统中所有的应用程序,所以勾子函数必须放在独立的动态链接库(DLL) 中。系统自动将包含"钩子回调函数"的DLL映射到受钩子函数影响的所有进程的地址空间中,即将这个DLL注入了那些进程。几点说明:(1)如果对于同一事件(如鼠标消息)既安装了线程勾子又安装了系统勾子,那么系统会自动先调用线程勾子,然后调用系统勾子。 (2)对同一事件消息可安装多个勾子处理过程,这些勾子处理过程形成了勾子链。当前勾子处理结束后应把勾子信息传递给下一个勾子函数。 (3)勾子特别是系统勾子会消耗消息处理时间,降低系统性能。只有在必要的时候才安装勾子,在使用完毕后要及时卸载。3、钩子类型       每一种类型的Hook可以使应用程序能够监视不同类型的系统消息处理机制。下面描述所有可以利用的Hook类型。1)、   WH_CALLWNDPROC和WH_CALLWNDPROCRET HooksWH_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 HookWH_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,它們不會被注射到任何行程位址空間。7)、   WH_JOURNALRECORD HookWH_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 HookWH_KEYBOARD_LL Hook监视输入到线程消息队列中的键盘消息。10)、WH_MOUSE HookWH_MOUSE Hook监视从GetMessage 或者 PeekMessage 函数返回的鼠标消息。使用这个Hook监视输入到消息队列中的鼠标消息。11)、WH_MOUSE_LL HookWH_MOUSE_LL Hook监视输入到线程消息队列中的鼠标消息。12)、WH_MSGFILTER 和 WH_SYSMSGFILTER HooksWH_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钟情況:1). 只要有个top-level、unowned 窗口被产生、起作用、或是被摧毁;2). 当Taskbar需要重画某个按钮;3). 当系统需要显示关于Taskbar的一个程序的最小化形式;4). 当目前的键盘布局状态改变;5). 当使用者按Ctrl+Esc去执行Task Manager(或相同级别的程序)。按照惯例,外壳应用程序都不接收WH_SHELL消息。所以,在应用程序能够接收WH_SHELL消息之前,应用程序必须调用SystemParametersInfo function注册它自己。
      

  3.   

    4、Hook消息的一个例子Hook全局键盘消息,从而可以知道用户按了哪些键,这种Hook消息的功能可以由以下函数来完成,该函数将一个新的Hook加入到原来的Hook链中,当某一消息到达后会依次经过它的Hook链再交给应用程序。 HHOOK SetWindowsHookEx(    int idHook,                      //Hook类型,例如WH_KEYBOARD,WH_MOUSE    HOOKPROClpfn,         //Hook处理过程函数的地址   HINSTANCE hMod,      //包含Hook处理过程函数的dll句柄(若在本进程可以为NULL)    DWORD dwThreadId,     //要Hook的线程ID,若为0,表示全局Hook所有);    这里需要提一下的就是如果是Hook全局的而不是某个特定的进程则需要将Hook过程编写为一个DLL,以便让任何程序都可以加载它来获取Hook过程函数。   
    而对于Hook API微软并没有提供直接的接口函数,也许它并不想让我们这样做,不过有2种方法可以完成该功能。第一种,修改可执行文件的IAT表(即输入表),因为在该表中记录了所有调用API的函数地址,则只需将这些地址改为自己函数的地址即可,但是这样有一个局限,因为有的程序会加壳,这样会隐藏真实的IAT表,从而使该方法失效。第二种方法是直接跳转,改变API函数的头几个字节,使程序跳转到自己的函数,然后恢复API开头的几个字节,在调用AP完成功能后再改回来又能继续Hook了,但是这种方法也有一个问题就是同步的问题,当然这是可以克服的,并且该方法不受程序加壳的限制。    下面将以一个Hook指定程序send函数的例子来详细描述如何Hook API,以达到监视程序发送的每个封包的目的。采用的是第二种方法,编写为一个dll。首先是一些全局声明,//本dll的handleHANDLE g_hInstance = NULL;
    //修改API入口为 mov eax, 00400000; jmp eax 是程序能跳转到自己的函数BYTE g_btNewBytes[8] = { 0xB8, 0x0, 0x0, 0x40, 0x0, 0xFF, 0xE0, 0x0 };
    //保存原API入口的8个字节 DWORD g_dwOldBytes[2][2] = { 0x0, 0x0, 0x0, 0x0 };
    //钩子句柄HHOOK   g_hOldHook = NULL;
    //API中 send 函数的地址 DWORD g_pSend = 0;
    //事务,解决同步问题 HANDLE g_hSendEvent = NULL;
    //自己的 send 函数地址,参数必须与API的send函数地址相同 int _stdcall hook_send( SOCKET s, const char *buf, int len, int flags );
    //要Hook的进程和主线程 ID 号 DWORD g_dwProcessID = 0; DWORD g_dwThreadID = 0;    从声明可以看出,我们会把API函数的首8个字节改为 mov eax, 00400000;jmp eax ,使程序能够跳转,只需获取我们自己的函数地址填充掉00400000即可实现跳转。而g_dwOldBytes是用来保存API开头原始的8个字节,在真正执行API函数是需要写回。还有一点,在声明新的函数时,该例中为hook_send,除了保正参数与API的一致外,还需要声明为__stdcall类型,表示函数在退出前自己来清理堆栈,因为这里是直接跳转到新函数处,所以必须自己清理堆栈。下面看主函数, 
    BOOL APIENTRY DllMain( HANDLE hModule, DWORD  ul_reason_for_call,  LPVOID lpReserved )
    {   
      If (ul_reason_for_call == DLL_PROCESS_ATTACH)   
      {  
         //获取本dll句柄      
         g_hInstance = hModule;           
         //创建事务
         g_hSendEvent = CreateEvent( NULL, FALSE, TRUE, NULL );     
         //重写API开头的8字节      
         HMODULE hWsock = LoadLibrary( "wsock32.dll" );      
         g_pSend = ( DWORD )GetProcAddress( hWsock, "send" ); 
         //保存原始字节      
         ReadProcessMemory( INVALID_HANDLE_VALUE, ( void * )g_pSend, ( void * )g_dwOldBytes[0], sizeof( DWORD )*2, NULL ); 
         //将00400000改写为我们函数的地址     
         *( DWORD* )( g_btNewBytes + 1 ) = ( DWORD )hook_send;      
         WriteProcessMemory( INVALID_HANDLE_VALUE, ( void * )g_pSend, ( void * )g_btNewBytes, sizeof( DWORD )*2, NULL ); 
       }    
       return TRUE;
    }    
    以上是dll的main函数,在被指定的程序加载的时候会自动运行dll的main函数来完成初始化,这里就是改写API的首地址来完成跳转。当然本程序是对于指定程序进行Hook,如果要进行全局Hook,可以在main函数中用GetModuleFileName函数来获取exe文件完整路径,判断当前进程是否是想要Hook的进程。写函数中使用INVALID_HANDLE_VALUE,表示写本进程。int _stdcall hook_send( SOCKET s, const char *buf, int len, int flags )
    {
       int nRet;  
       WaitForSingleObject( g_hSendEvent, INFINITE );   
       //恢复API头8个字节   
       WriteProcessMemory( INVALID_HANDLE_VALUE, ( void* )g_pSend, ( void* )g_dwOldBytes[0], sizeof( DWORD )*2, NULL ); 
       /* 这里可以添加想要进行的处理过程*/ 
       //真正执行API函数  
       nRet = send( s, buf, len, flags );    
       //写入跳转语句,继续Hook   
       WriteProcessMemory( INVALID_HANDLE_VALUE, (void*)g_pSend, (void*)g_btNewBytes, sizeof(DWORD)*2,NULL );   SetEvent( g_hSendEvent );   
       return nRet;
    }HOOK_API BOOL StartHook(HWND hWnd)
    {    
       //通过传入的窗口句柄获取线程句柄    
       g_dwThreadID = GetWindowThreadProcessId( hWnd, &g_dwProcessID );     
       //WH_CALLWNDPROC类型的Hook    
       g_hOldHook = SetWindowsHookEx( WH_CALLWNDPROC,  HookProc,  ( HINSTANCE ) g_hInstance,  g_dwThreadID );  
       if( g_hOldHook == NULL )       
         return FALSE;   
      
       return TRUE;
    }static LRESULT WINAPI HookProc( int nCode, WPARAM wParam, LPARAM lParam )
    {
       return CallNextHookEx( g_hOldHook, nCode, wParam, lParam ); 
    }HOOK_API void StopHook(void)
    {
       if(g_hOldHook != NULL)  
       {    
          WaitForSingleObject( g_hSendEvent, INFINITE );       
          HANDLE hProcess = NULL;      
          hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, g_dwProcessID); 
          DWORD dwOldProc;    
          DWORD dwNewProc;  
          //改变页面属性为读写   
          VirtualProtectEx( hProcess, ( void* )g_pSend, 8, PAGE_READWRITE, &dwOldProc ); 
          //恢复API的首8个字节
          WriteProcessMemory( hProcess, ( void* )g_pSend, ( void* )g_dwOldBytes[0], sizeof( DWORD )*2, NULL ); 
          //恢复页面文件的属性
          VirtualProtectEx( hProcess, ( void* )g_pSend, 8, dwOldProc, &dwNewProc ); 
          CloseHandle(g_hSendEvent); 
          UnhookWindowsHookEx( g_hOldHook );
        }
    }   可以看出,我们创建的Hook类型是WH_CALLWNDPROC类型,该类型的Hook在进程与系统一通信时就会被加载到进程空间,从而调用dll的main函数完成真正的Hook,而在SetWindowsHookEx函数中指定的HookProc函数将不作任何处理,只是调用CallNextHookEx将消息交给Hook链中下一个环节处理,因为这里SetWindowsHookEx的唯一作用就是让进程加载我们的dll。    以上就是一个最简单的Hook API的例子,该种技术可以完成许多功能。例如网游外挂制作过程中截取发送的与收到的封包即可使用该方法,或者也可以在Hook到API后加入木马功能,反向连接指定的主机或者监听某一端口,还有许多加壳也是用该原理来隐藏IAT表,填入自己的函数地址。5、关于HOOK效率
    使用HOOK 将会降低系统效率,因为它增加了系统处量消息的工作量。建议在必要时才使用HOOK,并在消息处理完成后立即移去该HOOK。建议只在调试时使用全局HOOK函数。全局HOOK函数将降低系统效率,并且会同其它使用该类HOOK的应用程序产生冲突。
      

  4.   

    倒塌……
    这是C的例子啊……
    有时间准备自己写个delphi的,不过至少也要等到考试之后了
    hook api对汇编要求很高,没事也不好乱写,麻烦死
      

  5.   

    看看孙鑫的VC++详解,应该是19或20章吧,有hook的讲解,用hook即可解决你的问题?但用时一定要小心谨慎
      

  6.   

    楼上已经说了,我还是要说就是HOOK中文叫钩子
      

  7.   

    要底层也不是在r3hook r0拦截硬盘设备的IRP包操作.
      

  8.   

    DELPHI
    http://blog.csdn.net/JPEXE/archive/2009/08/11/4433439.aspx