Windows下hook函数的几种方法

来源:互联网 发布:怎么学会编程 编辑:程序博客网 时间:2024/06/06 18:58

        讨论了Windows下hook函数的几种方法。提供了一个hook TextOutA的完整例子。通过CreateRemoteThread的方法把hook dll注入到一个普通的应用程序中。Hooking Imported Functions by name调用imported functions时的步骤/实现

    在程序中调用从其它模块引入的函数的方法和普通的函数调用有所不同。对于普通的函数调用,直接使用call address来调用即可,但是对于imported functions,在编译的时候compiler/link并不知道实际的函数实现会被加载到那个地址,函数实现在那个地址在运行的时候才会确定。对于imported functions,首先是call 引入表中的一个函数,在运行时再初始化引入表,使用jmp跳转到真实的函数实现。


引入表:

The PE file IMAGE_IMPORT_DESCRIPTOR structure, which holds all the information about functions imported from a specific DLL, has pointers to two arrays in the executable. These arrays are called import address tables (IATs), or sometimes thunk data arrays. The first pointer references the real IAT, which the program loader fixes up when the executable is loaded. The second pointer references the original IAT, which is untouched by the loader and lists the imported functions.


实现原理

找到PE文件的Image_Import_Descriptor结构
找到Original LAT和Real LAT.
通过要hook的函数的名字在Original LAT找到要hook的imported function在数组中的index.
保存并修改Real LAT在相应index的function address
(refer to John Robbins, BugsLayerUtil.dll)

Hooking Imported Functions by ordinal
原理和Hook Imported functions by name一样,只是是通过要hook的函数的ordinal在original LAT中找到index.


Hooking a function in this dll
当一个DLL是通过LoadLibrary载入的时候,我们无法通过hook imported function的方法的hook它中的function。有两种可能的办法处理这种情况:

第一种方法,遍历进程空间,发现call指定函数的地方替换为call hookFunction. 太麻烦,而且不安全。


第二种方法,改写要hook的函数FuncA。比较好的方法

实现HookFuncA,最后的实现垫入n个nop.
找到要hook的函数FuncA的绝对地址,改写前5个字节为jmp hookFuncA(假定前5个字节为n个完整的指令)
把FuncA的前5个字节拷贝到hookFuncA的后面,在加上一条指令jmp funcA+5.

 ----Code of HookDLL.dll, 可以通过CreateRemoteThread的方法把hook dll注入到一个普通的应用程序中。

 

  1. // HookDLL.cpp : Defines the entry point for the DLL application.
  2. //
  3. #include "stdafx.h"
  4. #include "HookDLL.h"
  5. #include "Log.h"
  6. //forward declare.
  7. LRESULT WINAPI InstallTextoutHook();
  8. LRESULT WINAPI UninstallTextoutHook();
  9. BOOL APIENTRY DllMain( HANDLE hModule, 
  10.                        DWORD  ul_reason_for_call, 
  11.                        LPVOID lpReserved
  12.       )
  13. {
  14.     switch (ul_reason_for_call)
  15.  {
  16.   case DLL_PROCESS_ATTACH:   
  17.    if (InstallTextoutHook())
  18.    {
  19.     WriteLog("Install hook success./n");
  20.    }else
  21.    {
  22.     WriteLog("Intall hook failed./n");
  23.    }
  24.    break;
  25.   case DLL_THREAD_ATTACH:
  26.    break;
  27.   case DLL_THREAD_DETACH:
  28.    break;
  29.   case DLL_PROCESS_DETACH:
  30.    if (UninstallTextoutHook())
  31.    {
  32.     WriteLog("Uninstall hook success./n");
  33.    }else
  34.    {
  35.     WriteLog("Unintall hook failed./n");
  36.    }
  37.    break;
  38.     }
  39.     return TRUE;
  40. }
  41. #define DWORD_PTR DWORD*
  42. #define __LOCAL_SIZE 40h
  43. #define NAKED_PROLOG()                                                 /
  44.     DWORD_PTR dwRet ;                                                  /
  45.     DWORD_PTR dwESI ;                                                  /
  46.     {                                                                  /
  47.         __asm PUSH  EBP                 /* Set up the standard frame.*//
  48.         __asm MOV   EBP , ESP                                          /
  49.         __asm SUB   ESP , __LOCAL_SIZE  /* Save room for the local   *//
  50.                                         /* variables.                *//
  51.         __asm MOV   EAX , EBP           /* EBP has the stack coming  *//
  52.                                         /* into the fn. in it.       *//
  53.         __asm ADD   EAX , 4             /* Account for PUSH EBP      *//
  54.         __asm MOV   EAX , [EAX]         /* Get return address.       *//
  55.         __asm MOV   [dwRet] , EAX       /* Save return address.      *//
  56.         __asm MOV   [dwESI] , ESI       /* Save ESI so chkesp in dbg *//
  57.                                         /* builds works.             *//
  58.     }
  59. // The common epilog part that can be shared between the stdcall and
  60. // cdecl hook functions.
  61. #define EPILOG_COMMON()                                                /
  62.     {                                                                  /
  63.         __asm MOV   ESI , [dwESI]       /* Restore ESI.              *//
  64.         __asm ADD   ESP , __LOCAL_SIZE  /* Take away local var space *//
  65.         __asm MOV   ESP, EBP            /* Restore standard frame.   *//
  66.         __asm POP   EBP                                                /
  67.     }
  68. #define COPY_CODE_LENGTH 5
  69. BYTE g_abOriCode[COPY_CODE_LENGTH];
  70. BYTE g_abJmpCode[COPY_CODE_LENGTH];
  71. PROC g_oriTextout;
  72. BOOL g_blHooked = FALSE;
  73. LRESULT WINAPI InstallTextoutHook()
  74. {
  75.  if (g_blHooked)
  76.   return TRUE;
  77.  //Get TextOutA's address.
  78.  HMODULE hGdi32 = ::LoadLibrary(_T("Gdi32.dll"));
  79.  g_oriTextout = GetProcAddress(hGdi32, _T("TextOutA"));
  80.  if (NULL == g_oriTextout)
  81.   return FALSE;
  82.  //Get the hook'a address.
  83.  HMODULE hModule = GetModuleHandle(_T("HookDLL.dll"));
  84.  if (NULL == hModule)
  85.   return FALSE;
  86.  DWORD dwHookAddr = NULL;
  87.  __asm
  88.  {
  89.   mov esi, offset HookLabel;
  90.   mov edi, 0x10000000;//0x10000000 is the dll's base address.
  91.   sub esi, edi;
  92.   add esi, hModule;
  93.   mov [dwHookAddr], esi;
  94.  }
  95.  //Get the NOP's address.
  96.  DWORD dwNOPAddr = NULL;
  97.  __asm
  98.  {
  99.   mov esi, offset NOPLabel;
  100.   mov edi, 0x10000000;//0x10000000 is the dll's base address.
  101.   sub esi, edi;
  102.   add esi, hModule;
  103.   mov [dwNOPAddr], esi;
  104.  }
  105.  //Save the first 5 byte of TextOutA to g_abOriCode
  106.  __asm
  107.  {
  108.   mov esi, g_oriTextout;
  109.   lea edi, g_abOriCode;
  110.   cld;
  111.   movsd;
  112.   movsb;
  113.  }
  114.  //Generate the jmp Hook function.
  115.  g_abJmpCode[0] = 0xe9;
  116.  __asm
  117.  {
  118.   mov eax, dwHookAddr;
  119.   mov ebx, g_oriTextout;
  120.   add ebx, 5;
  121.   sub eax, ebx;
  122.   mov dword ptr[g_abJmpCode+1], eax;
  123.  }
  124.  //Write the jump instruction to the textoutA.
  125.  DWORD dwProcessId = GetCurrentProcessId();
  126.  HANDLE hProcess = OpenProcess (PROCESS_ALL_ACCESS, 
  127.                     FALSE, dwProcessId); 
  128.  if (NULL == hProcess)
  129.   return FALSE;
  130.  DWORD dwOldFlag;
  131.  VirtualProtectEx(hProcess, g_oriTextout, 5, PAGE_READWRITE, &dwOldFlag);
  132.  WriteProcessMemory(hProcess, g_oriTextout, g_abJmpCode, sizeof(g_abJmpCode), NULL);
  133.  VirtualProtectEx(hProcess, g_oriTextout, 5, dwOldFlag, NULL);
  134.  //Write g_abOriTextout to the end of Hook function(NOP addr), then write the jmp instruction. 
  135.  VirtualProtectEx(hProcess, (LPVOID)dwNOPAddr, 10, PAGE_READWRITE, &dwOldFlag);
  136.  WriteProcessMemory(hProcess, (LPVOID)dwNOPAddr, g_abOriCode, sizeof(g_abOriCode), NULL);
  137.  //Generate the jmp TextoutA + 5
  138.  __asm
  139.  { 
  140.   mov eax, g_oriTextout;
  141.   mov ebx, dwNOPAddr;
  142.   add ebx, 5;
  143.   sub eax, ebx;
  144.   mov dword ptr[g_abJmpCode+1], eax;
  145.  }
  146.  WriteProcessMemory(hProcess, (LPVOID)(dwNOPAddr+5), g_abJmpCode, sizeof(g_abJmpCode), NULL);
  147.  VirtualProtectEx(hProcess, (LPVOID)dwNOPAddr, 10, dwOldFlag, NULL);
  148.  g_blHooked = TRUE;
  149.  if(TRUE)
  150.   return TRUE;
  151. HookLabel:
  152.  NAKED_PROLOG ( ) ;
  153.  int nx, ny;
  154.   LPCSTR lp;
  155.  lp = NULL;
  156.  _asm
  157.  {
  158.   mov esi, ebp;
  159.   add esi, 0Ch;
  160.   lea edi, nx;
  161.   movsd;
  162.   lea edi, ny;
  163.   movsd;
  164.   
  165.   lea edi, lp;
  166.   movsd;
  167.  }
  168.  WriteLog_F("Try to ouput /"%s/" at (%d,%d)/n", lp, nx, ny);
  169.     // Do the common epilog.
  170.     EPILOG_COMMON ( ) ;
  171. NOPLabel:
  172.  _asm NOP
  173.  _asm NOP
  174.  _asm NOP
  175.  _asm NOP
  176.  _asm NOP
  177.  _asm NOP
  178.  _asm NOP
  179.  _asm NOP
  180.  _asm NOP
  181.  _asm NOP
  182.  _asm NOP
  183. }
  184. LRESULT WINAPI UninstallTextoutHook()
  185.  if (!g_blHooked)
  186.   return FALSE;
  187.  //Restore the first 5 bytes code of TextOutA
  188.  DWORD dwProcessId = GetCurrentProcessId();
  189.  HANDLE hProcess = OpenProcess (PROCESS_ALL_ACCESS, 
  190.                     FALSE, dwProcessId); 
  191.  if (NULL == hProcess)
  192.   return FALSE;
  193.  DWORD dwOldFlag;
  194.  VirtualProtectEx(hProcess, g_oriTextout, 5, PAGE_READWRITE, &dwOldFlag);
  195.  WriteProcessMemory(hProcess, g_oriTextout, g_abOriCode, sizeof(g_abOriCode), NULL);
  196.  VirtualProtectEx(hProcess, g_oriTextout, 5, dwOldFlag, NULL);
  197.  g_blHooked = FALSE;
  198.  return TRUE;
  199. }
原创粉丝点击