zoukankan      html  css  js  c++  java
  • 自己实现函数钩子

    看了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
  • 相关阅读:
    windows版本nginx帮助文档
    angular渲染超链接bug
    ionic踩过的一些坑
    ionic项目中使用自定义图标打包出现错误-broken import
    ionic项目中使用自定义图标
    ionic3运行时gradle下载失败的解决方法
    阿里云菜鸡上云实例
    如何用手机控制pc鼠标——Java开发与学习记实
    LeetCode刷题笔记---6月
    编译原理复习题
  • 原文地址:https://www.cnblogs.com/nanshu/p/2887001.html
Copyright © 2011-2022 走看看