自己实现函数钩子

看了detour的原理,心里想着自己能不能动手模拟一个。

比划的时候发现了一些细节问题。detour将函数的前几个字节(刚开始看文档的时候说5个字节)替换成jmp __

。但是发生机器码截断整么办?可以在vs的反汇编窗口看到每条汇编指令的机器码,每条指令的机器码长度并不是固定的,我想detour并不是固定复制5个字节这么简单(目前还没研究detour源码)。所以退而求其次(这是一个学习性质的实验),改变设计。

我选择在自己定义的函数执行完之后将修改的代码再改回去,跳回原函数运行。看起来是这样的:

绘图1

而且自定义函数是不知道自己返回到的是traceBack的。

即原函数总是会被执行。这就变成了钩子,而且只能钩住一次,因为我把被修改区域复原了。

不过暂且就不计较这个了(这是一个学习性质的实验)。

真正自己实现的时候才发现一些有趣的细节问题,比如jmp对应有多个机器码,选择哪一个,代码页原来是不允许写的,要VirtualProtect函数修改,traceBack不能return只能jmp,为了保证栈平衡需要写成__declspec(naked)之类。

还有我修改了15个字节,三个句子:

push __

push __

jmp __

第一个push压入的信息提供给traceBack,好让他能找到修改前的代码拷回去。

第二个push压入traceBack的地址,这样跳转到traceBack就对自定义函数(hook)是透明的了。

最后转跳,是一个近转跳,所以还要计算相对位移。

效果:

   1: #include "detour.h"
   2: #include <stdio.h>
   3: #include <windows.h>
   4:  
   5: #pragma optimize("",off)
   6:  
   7: void hook()
   8: {
   9:     printf("detoured\n");
  10: }
  11:  
  12: int main()
  13: {
  14:     detourInit();
  15: //---------------------------------------------------------
  16:  
  17:     detour( Sleep,hook );
  18:  
  19:     Sleep(1);
  20:  
  21:     system("pause");
  22:     return 0;
  23: }

PS: detour.h 即我自己实现的源码。(代码在下面)

结果:

360软件小助手截图20130131143322

Sleep就被勾住了。但是再次调用Sleep就是正常的。

 

具体代码:

detour.h ( 其实应该起名hook的 )

   1: #ifndef __DETOUR_H__
   2: #define __DETOUR_H__
   3:  
   4: #pragma once
   5:  
   6: #include <windows.h>
   7: #include <assert.h>
   8: #include <stdlib.h>
   9:  
  10: typedef unsigned char u8;
  11:  
  12: //push (下标) + push (traceBack地址) + jmp (hook) 覆盖15个字节
  13: #define TBSIZE 15
  14: #define INFOSIZE 5
  15: #define REPSIZE 15
  16:  
  17: //machine code
  18: #define MC_FARJMP    0xE9
  19: #define MC_PUSH        0x68
  20:  
  21: struct s_detour
  22: {
  23:     void *oldFunc;
  24:     void *hook;
  25:     u8 backUp[TBSIZE];
  26: };
  27:  
  28: s_detour gInfo[INFOSIZE];
  29:  
  30: void detourInit()
  31: {
  32:     assert( sizeof(gInfo) > sizeof(s_detour) );
  33:     memset( gInfo,0,sizeof(gInfo) );
  34: }
  35:  
  36: LPDWORD ftb_old = new DWORD;
  37: BOOL ftb_res;
  38: void* ftb_addr;
  39: __declspec(naked) void traceBack( int tn )
  40: {
  41:     //restore
  42:     //ps:只能jmp不能return
  43:     __asm
  44:     {
  45:         push 0x1        //假装地址填充.使得tn能够正确访问
  46:         push ebp
  47:         mov ebp,esp
  48:     }
  49:  
  50:     assert( tn >= 1 && tn < INFOSIZE );
  51:  
  52:     //#the return would work?
  53:     ftb_res = VirtualProtect( gInfo[tn].oldFunc,REPSIZE,PAGE_EXECUTE_READWRITE,ftb_old );
  54:     assert( ftb_res );
  55:  
  56:     memcpy( gInfo[tn].oldFunc,gInfo[tn].backUp,REPSIZE );
  57:  
  58:     ftb_res = VirtualProtect( gInfo[tn].oldFunc,REPSIZE,*ftb_old,ftb_old );
  59:     assert( ftb_res );
  60:  
  61:     //ready to back
  62:     ftb_addr = (void*)gInfo[tn].oldFunc;
  63:     gInfo[tn].oldFunc = NULL;
  64:     __asm
  65:     {
  66:         mov esp,ebp
  67:         pop ebp
  68:         pop eax        //pop 0x1
  69:         pop eax        //balance stack  pop tn
  70:  
  71:         mov eax,ftb_addr
  72:         jmp eax
  73:     }
  74: }
  75:  
  76: //register
  77: bool detour( void *oldFunc,void *hook )
  78: {
  79:     if( oldFunc == NULL || hook == NULL )
  80:         return false;
  81:  
  82:     int tn;
  83:     for( tn = 1;tn < INFOSIZE;++tn )
  84:     {
  85:         if( gInfo[tn].oldFunc == NULL )        //could be used
  86:             break;
  87:     }
  88:     if( tn == INFOSIZE )
  89:         return false;
  90:  
  91:     gInfo[tn].oldFunc = oldFunc;
  92:     gInfo[tn].hook = hook;
  93:  
  94:     LPDWORD lpOld = new DWORD;
  95:     BOOL res = VirtualProtect( oldFunc,REPSIZE,PAGE_EXECUTE_READWRITE,lpOld );
  96:     assert( res );
  97:  
  98:     memcpy( gInfo[tn].backUp,oldFunc,REPSIZE );    //back up
  99:  
 100:     //replace
 101:     u8 *pf = (u8*)oldFunc;
 102:     pf[0] = MC_PUSH;
 103:     *((int*)((int)pf+1)) = tn;                //压入标记
 104:  
 105:     pf = (u8*)( (int)pf + 5 );
 106:     pf[0] = MC_PUSH;
 107:     *((int*)((int)pf+1)) = (int)traceBack;    //压入traceBack地址
 108:  
 109:     pf = (u8*)( (int)pf + 5 );
 110:     pf[0] = MC_FARJMP;
 111:     *((int*)((int)pf+1)) = (int)((int)hook - ((int)pf) - 5 );        //near jmp 相对地址
 112:  
 113:     res = VirtualProtect( oldFunc,REPSIZE,*lpOld,lpOld );
 114:     assert( res );
 115:     free( lpOld );
 116:  
 117:     return true;
 118: }
 119:  
 120: #endif
posted @ 2013-01-31 14:36  南树  阅读(1502)  评论(5编辑  收藏  举报