API hook 原理与Windows hook 应用

来源:互联网 发布:微软程序员面试题 编辑:程序博客网 时间:2024/04/29 07:10

API hook 原理与Windows hook 应用

分类: 系统程序 3679人阅读 评论(3) 收藏 举报
hookapiwindowsattributesdescriptorwinapi

目录(?)[+]


CSDN 上图片很麻烦,请到百度文库直接看Word 文档,图片比这个全http://wenku.baidu.com/view/0946ce1155270722192ef74f.html

Hook 字面的意思是钩子,但是实际上更像提供一种过滤,或者说修改消息处理,或者API的机制。

API hook 

什么是APIhook呢,其实就是将API调用的地方进行了转移,或者重新实现的一种技术。这种技术不仅仅可以用在windows 上,其他OS上一样可以使用,我曾经在brew mp 上的手机开发上测试过API hook。所以这里主要只讲windows 下的API hook。 APIhook 基本思路都是想办法替换原来的API,使函数的调用关系,直接进行转换, 典型的如下




可以看到client 端要调用的FunctionA,在A进入之后,就调用了FunctionB , Function B 中回到FunctionA 做具体A应该做的事情,从A返回之后,FunctionB 然后在返回给Client,那么在FunctionB 中使用FunctionA的前后,它自己都可以干自己想要干的事情,这样就是一个APIhook, 下面我们主要讨论Windows 上最常用的两种API hook 策略。

A)IAT 替换API hook 

所谓的IAT (import  address table ) 这个是PE文件中一个重要的部分,这里我们不详细讨论PE文件的构成,因为这个本来就可以写一张内容。我们主要了解的是PE如何在一个已经加载在内存中的PE文件如何找到它的IAT

(1) 为什么要找IAT

          一个模块,可能依赖别的API,也可能提供API,供别的模块调用,MS为了在PE文件中,分别用导入表和导出表进行描述两种关系。导入表IAT 主要是描述了这个模块依赖那些其他模块提供的API。 而导出表EAT 是描述这个模块中都提供那些函数供别的模块调用。如果我们在某个模块的导入表中替换它所依赖的函数,那么这样就可以实现API hook.

(2) 如何找IAT

 要找IAT 要稍微了解一下PE文件的结构,

 


 

一个PE 文件的大概长相就是这样,我们要找的IAT,就是要先找到PE文件可选头,在这个数据结构中,描述了IAT 描述的位置在那里,然后给距IAT descriptor 就可以找到IAT了。

[cpp] view plaincopy
  1. BOOL TestFunctionInIAT( HMODULE hModule, ULONG FunctionAddress )  
  2. {  
  3.     BOOL bReturn = FALSE;  
  4.     unsigned char *pBaseAddr = reinterpret_cast<unsigned char *>(hModule);  
  5.   
  6.     // 获取DOS header 的位置  
  7.     PIMAGE_DOS_HEADER pDosHeader = reinterpret_cast<PIMAGE_DOS_HEADER>(pBaseAddr);  
  8.   
  9.     // 获取NTImage header 的位置  
  10.     PIMAGE_NT_HEADERS pNtHeader = reinterpret_cast<PIMAGE_NT_HEADERS>(  
  11.         pBaseAddr + pDosHeader->e_lfanew );  
  12.   
  13.     // 获取 PE option header的位置  
  14.     PIMAGE_OPTIONAL_HEADER pPEOptionHeader = &pNtHeader->OptionalHeader;  
  15.   
  16.     // 获取导入表的目录结构  
  17.     PIMAGE_DATA_DIRECTORY pIATDataDirectory = &(pPEOptionHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]);  
  18.   
  19.     // 获取导入表 descriptor  
  20.     PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor = reinterpret_cast<PIMAGE_IMPORT_DESCRIPTOR>(  
  21.         pBaseAddr + pIATDataDirectory->VirtualAddress );  
  22.   
  23.     // 从pImportDescriptor 开始是一堆导入表,一张接着一张,直到  
  24.     // 导入表的名字为空为止,其实就是对应Windows 的一个dll,有几张表,就表示  
  25.     // 该模块依赖几个dll 的导出函数, Name 字段是dll的名称的相对虚拟地址  
  26.     while ( pImportDescriptor->Name != 0 )  
  27.     {  
  28.         // thunk data 就是表示导入dll 中函数描述  
  29.         PIMAGE_THUNK_DATA pThunkData = reinterpret_cast<PIMAGE_THUNK_DATA>(  
  30.             pBaseAddr + pImportDescriptor->FirstThunk);  
  31.         while( pThunkData->u1.Function != 0 )  
  32.         {  
  33.              ULONG *ppfn = ( ULONG *)&pThunkData->u1.Function;   
  34.              if ( *ppfn == FunctionAddress )  
  35.              {  
  36.               
  37.                  bReturn = TRUE;  
  38.                  break;  
  39.              }  
  40.              ++pThunkData;  
  41.         }  
  42.         ++pImportDescriptor;  
  43.     }  
  44.   
  45.     return bReturn;  
  46. }  
  47.   
  48. PROC g_CreateFunc = NULL;  
  49.   
  50. typedef BOOL (WINAPI *PCreateProcessW)(  
  51.                                        __in_opt    LPCWSTR lpApplicationName,  
  52.                                        __inout_opt LPWSTR lpCommandLine,  
  53.                                        __in_opt    LPSECURITY_ATTRIBUTES lpProcessAttributes,  
  54.                                        __in_opt    LPSECURITY_ATTRIBUTES lpThreadAttributes,  
  55.                                        __in        BOOL bInheritHandles,  
  56.                                        __in        DWORD dwCreationFlags,  
  57.                                        __in_opt    LPVOID lpEnvironment,  
  58.                                        __in_opt    LPCWSTR lpCurrentDirectory,  
  59.                                        __in        LPSTARTUPINFOW lpStartupInfo,  
  60.                                        __out       LPPROCESS_INFORMATION lpProcessInformation);  
  61.   
  62. BOOL WINAPI MyCreateProcessW(  
  63.                              __in_opt    LPCWSTR lpApplicationName,  
  64.                              __inout_opt LPWSTR lpCommandLine,  
  65.                              __in_opt    LPSECURITY_ATTRIBUTES lpProcessAttributes,  
  66.                              __in_opt    LPSECURITY_ATTRIBUTES lpThreadAttributes,  
  67.                              __in        BOOL bInheritHandles,  
  68.                              __in        DWORD dwCreationFlags,  
  69.                              __in_opt    LPVOID lpEnvironment,  
  70.                              __in_opt    LPCWSTR lpCurrentDirectory,  
  71.                              __in        LPSTARTUPINFOW lpStartupInfo,  
  72.                              __out       LPPROCESS_INFORMATION lpProcessInformation)  
  73. {  
  74.   
  75.     MessageBoxW(NULL, lpCommandLine, L"CreateProcessW", MB_OK);  
  76.   
  77.     return ((PCreateProcessW)g_CreateFunc)(  
  78.         lpApplicationName,   
  79.         lpCommandLine,  
  80.         lpProcessAttributes,  
  81.         lpThreadAttributes,  
  82.         bInheritHandles,  
  83.         dwCreationFlags,  
  84.         lpEnvironment,  
  85.         lpCurrentDirectory,  
  86.         lpStartupInfo,  
  87.         lpProcessInformation);  
  88. }  
  89.   
  90. int _tmain(int argc, _TCHAR* argv[])  
  91. {  
  92.     HMODULE hModule = NULL;  
  93.     GetModuleHandleEx(  
  94.         GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,  
  95.         (LPCTSTR)TestFunctionInIAT,  
  96.         &hModule);  
  97.     BOOL bReturn = TestFunctionInIAT( hModule , (ULONG_PTR)CreateProcessW );  
  98.     if ( bReturn )  
  99.     {  
  100.         printf("Found address CreateProcessW!\n");  
  101.     }  
  102.     else  
  103.     {  
  104.         printf("found failed!\n");  
  105.     }  
  106.   
  107.     g_CreateFunc = install_api_hook(hModule, "kernel32.dll", (PROC)CreateProcessW, (PROC)MyCreateProcessW);  
  108.       
  109.     wchar_t szProcessName[] = L"notepad.exe";  
  110.     STARTUPINFO si = {sizeof(si)};  
  111.     PROCESS_INFORMATION pi;   
  112.     CreateProcessW(NULL,  
  113.         szProcessName,  
  114.         NULL,  
  115.         NULL,  
  116.         FALSE,  
  117.         0,  
  118.         NULL,  
  119.         NULL,  
  120.         &si,  
  121.         &pi);  
  122.   
  123.     return 0;  
  124. }  
  125.   
  126. PROC install_api_hook(  
  127.                       HMODULE hHookModule,  
  128.                       const char * szDllName,   
  129.                       PROC pfnHookFunAddr,  
  130.                       PROC pfnNewFundAddr  
  131.                       )  
  132. {  
  133.     PROC pOrigFunc = NULL;  
  134.   
  135.     unsigned char *pBaseAddr =   
  136.         reinterpret_cast<unsigned char *>(hHookModule);  
  137.   
  138.     PIMAGE_DOS_HEADER pDosHeader =  
  139.         reinterpret_cast<PIMAGE_DOS_HEADER>(pBaseAddr);  
  140.   
  141.     PIMAGE_NT_HEADERS pNtHeader =   
  142.         reinterpret_cast<PIMAGE_NT_HEADERS>(  
  143.         pBaseAddr + pDosHeader->e_lfanew );  
  144.   
  145.     PIMAGE_OPTIONAL_HEADER pPEOptionHeader =   
  146.         &pNtHeader->OptionalHeader;  
  147.   
  148.     PIMAGE_DATA_DIRECTORY pIATDataDirectory =   
  149.         &(pPEOptionHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]);  
  150.   
  151.     PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor =   
  152.         reinterpret_cast<PIMAGE_IMPORT_DESCRIPTOR>(  
  153.         pBaseAddr + pIATDataDirectory->VirtualAddress );  
  154.   
  155.     for ( ; pImportDescriptor->Name; pImportDescriptor++ )    
  156.     {    
  157.         const char* pszModName =  
  158.             reinterpret_cast<const char*>(  
  159.             pBaseAddr + pImportDescriptor->Name);    
  160.         if ( 0 == lstrcmpiA( pszModName, szDllName ) )    
  161.         {  
  162.             break;    
  163.         }  
  164.     }    
  165.   
  166.     if ( 0 == pImportDescriptor->Name )  
  167.     {  
  168.         return pOrigFunc;  
  169.     }  
  170.   
  171.     PIMAGE_THUNK_DATA pThunkData =   
  172.         reinterpret_cast<PIMAGE_THUNK_DATA>(  
  173.         pBaseAddr + pImportDescriptor->FirstThunk);  
  174.     while( pThunkData->u1.Function != 0 )  
  175.     {  
  176.         PROC *ppFunc = reinterpret_cast<PROC*>(  
  177.             &pThunkData->u1.Function);  
  178.         if ( *ppFunc == pfnHookFunAddr )  
  179.         {  
  180.             DWORD dwOldProtect = 0;    
  181.             VirtualProtect( ppFunc, sizeof(PROC), PAGE_READWRITE, &dwOldProtect );    
  182.               
  183.             pOrigFunc = *ppFunc;  
  184.             CopyMemory(ppFunc, &pfnNewFundAddr, sizeof(PROC));  
  185. //          SIZE_T stMemorySize = 0;  
  186. //          WriteProcessMemory(   
  187. //              GetCurrentProcess(),  
  188. //              ppFunc,  
  189. //              &uNewFundAddr,  
  190. //              sizeof(*ppFunc),  
  191. //              &stMemorySize);  
  192.             VirtualProtect( ppFunc, sizeof(PROC), dwOldProtect, 0 );    
  193.             break;  
  194.         }  
  195.     }  
  196.   
  197.     return pOrigFunc;     
  198. }  

另外要获得导入表描述符,只要使用这个API ImageDirectoryEntryToData 就可以了,

但是这个API是有依赖的。我们使用Dumpbin,或者TC 都可以看到这个exe 确实使用kernel32 dll, 所以,我们下一步要替换我们的自己MyCreateProcessW. 将上面函数稍微优化一下,修改一下,就可以了,



然后在看这样的使用,那么一定会在CreateProcess 的时候,先弹出一个对话框,这样,就是我们的效果。

BJMP 指令替换

现在你在网上搜索一下,说JMP指令替换的,一般都说保存函数入口处5个字节,其实这是相对地址跳转,当然了,如果使用了绝对跳转,那么就不至5个字节了,如果使用相对短地址跳转,当然少于5个字节,这个部分可以参看80x86 汇编instruction set, operator code.

这里我们也采用相对地址跳转,但是最后会说明如何用短跳转实现API hook, 因为这个很有用。至于是什么跳转,主要是他们跳转地址范围不同。相对地址跳转-2G2G

跳转的hook 的原理

第一种, 就是进入FunctionA 就跳转到FunctionB,做完B应该做的事情之后,将FunctionA的入口处的JMP去掉,回复到FunctionA原来的样子。 然后在Function B 中调用FunctionA

当然这个地方你也可以不调用,根据你的功能决定, 然后再在FunctionA的入口布置JMP指令。 最后就是返回Caller

我们看看如何实现,  这里补充一下,如何计算JMP指令后的operand. 就是跳转的相对地址。

Label1:

     Mov EAX, EAX

     Mov Edi, edi

...............................

Label2:

     JMP Label1

Label3:

     XOR EcX, EcX

那么如果是相对地址跳转JMP label1 汇编机器指令的以后,label1 这个位置的值是多少呢,因为只有知道这个如何计算,那么我们就知道怎么hardcode JMP 指令。因为计算机当执行

JMP Label1 的时候,地址译码机构已经完成了这条指令的译码,并将其送到了可执行单元,在可执行单元还没有开始执行的时候,地址译码机构不能闲着,已经将地址Label3已经装载到EIP中,当可执行单元进行执行JMP Label1的时候,其实是将EIP + JMP 指令operand 合成出来的,那么就有了下面的等式:

Label1 = EIP + operand;

EIP 等于label3 那么 

label1= Lebel3+ operand, 

那么

operand = Label1 - Label3.

这个是关键。

通常我们是不知道label3 的地址的,但是我们一定能知道lable2 的地址,这个地方就是我们要放JMP 指令的位置,如果我们放相对跳转的JMP 指令,该指令占用5byte. 那么label3 就可以通过lable2 + JMP 指令的长度 计算出来。


[cpp] view plaincopy
  1. // JmpHook.cpp : Defines the entry point for the console application.  
  2. //  
  3.   
  4. #include "stdafx.h"  
  5. #include <windows.h>  
  6.   
  7. unsigned char g_StubCode[6] = {0x0};  
  8. PROC g_CreateFunc = 0;  
  9. void restore_hook(PROC pfnOrigAddr);  
  10. void set_hook(PROC pfnOrigAddr, PROC pfnNewAddr );  
  11.   
  12. typedef BOOL (WINAPI *PCreateProcessW)(  
  13.                                        __in_opt    LPCWSTR lpApplicationName,  
  14.                                        __inout_opt LPWSTR lpCommandLine,  
  15.                                        __in_opt    LPSECURITY_ATTRIBUTES lpProcessAttributes,  
  16.                                        __in_opt    LPSECURITY_ATTRIBUTES lpThreadAttributes,  
  17.                                        __in        BOOL bInheritHandles,  
  18.                                        __in        DWORD dwCreationFlags,  
  19.                                        __in_opt    LPVOID lpEnvironment,  
  20.                                        __in_opt    LPCWSTR lpCurrentDirectory,  
  21.                                        __in        LPSTARTUPINFOW lpStartupInfo,  
  22.                                        __out       LPPROCESS_INFORMATION lpProcessInformation);  
  23.   
  24. BOOL WINAPI MyCreateProcessW1(  
  25.                              __in_opt    LPCWSTR lpApplicationName,  
  26.                              __inout_opt LPWSTR lpCommandLine,  
  27.                              __in_opt    LPSECURITY_ATTRIBUTES lpProcessAttributes,  
  28.                              __in_opt    LPSECURITY_ATTRIBUTES lpThreadAttributes,  
  29.                              __in        BOOL bInheritHandles,  
  30.                              __in        DWORD dwCreationFlags,  
  31.                              __in_opt    LPVOID lpEnvironment,  
  32.                              __in_opt    LPCWSTR lpCurrentDirectory,  
  33.                              __in        LPSTARTUPINFOW lpStartupInfo,  
  34.                              __out       LPPROCESS_INFORMATION lpProcessInformation)  
  35. {  
  36.   
  37.     restore_hook(g_CreateFunc);  
  38.   
  39.     MessageBoxW(NULL, lpCommandLine, L"CreateProcessW", MB_OK);  
  40.   
  41.     BOOL bRetCode = ((PCreateProcessW)g_CreateFunc)(  
  42.         lpApplicationName,   
  43.         lpCommandLine,  
  44.         lpProcessAttributes,  
  45.         lpThreadAttributes,  
  46.         bInheritHandles,  
  47.         dwCreationFlags,  
  48.         lpEnvironment,  
  49.         lpCurrentDirectory,  
  50.         lpStartupInfo,  
  51.         lpProcessInformation);  
  52.   
  53.     set_hook(g_CreateFunc, (PROC)MyCreateProcessW1);  
  54.   
  55.     return bRetCode;  
  56. }  
  57.   
  58.   
  59. void set_hook(PROC pfnOrigAddr, PROC pfnNewAddr )  
  60. {  
  61.     unsigned char *pSrcAddr =   
  62.         reinterpret_cast<unsigned char *>( pfnOrigAddr );  
  63.   
  64.     unsigned char *pDestAddr =   
  65.         reinterpret_cast<unsigned char *>( pfnNewAddr );  
  66.   
  67.     ULONG uOperand = static_cast<ULONG>  
  68.         ( pDestAddr - (pSrcAddr + 5) );  
  69.   
  70.     CopyMemory(g_StubCode, pSrcAddr, 5);  
  71.   
  72.     DWORD dwOldProtect = 0;    
  73.     VirtualProtect( pSrcAddr, 5, PAGE_READWRITE, &dwOldProtect );   
  74.   
  75.     unsigned char szJMPCode[5] = {0xE9};  
  76.     CopyMemory(&szJMPCode[1], &uOperand, 4);  
  77.     CopyMemory(pSrcAddr, szJMPCode, 5 );  
  78.   
  79.     VirtualProtect( pSrcAddr, 5, dwOldProtect, NULL );   
  80. }  
  81.   
  82. void restore_hook(PROC pfnOrigAddr)  
  83. {  
  84.     unsigned char *pSrcAddr =   
  85.         reinterpret_cast<unsigned char *>( pfnOrigAddr );  
  86.     DWORD dwOldProtect = 0;    
  87.     VirtualProtect( pSrcAddr, 5, PAGE_READWRITE, &dwOldProtect );   
  88.     CopyMemory(pSrcAddr, g_StubCode, 5);  
  89.     VirtualProtect( pSrcAddr, 5, dwOldProtect, NULL );   
  90. }  
  91.   
  92. int _tmain(int argc, _TCHAR* argv[])  
  93. {  
  94.     g_CreateFunc = GetProcAddress(GetModuleHandle(L"kernel32.dll"),"CreateProcessW");  
  95.       
  96.     set_hook(g_CreateFunc, (PROC)MyCreateProcessW1);  
  97.     wchar_t szProcessName[] = L"notepad.exe";  
  98.     STARTUPINFO si = {sizeof(si)};  
  99.     PROCESS_INFORMATION pi;   
  100.     CreateProcessW(NULL,  
  101.         szProcessName,  
  102.         NULL,  
  103.         NULL,  
  104.         FALSE,  
  105.         0,  
  106.         NULL,  
  107.         NULL,  
  108.         &si,  
  109.         &pi);  
  110.   
  111.     return 0;  
  112. }  

基本上就是这个样子了,但是这个有一个致命的缺点,就是如果是多线程,那么可能hook 不到,怎么回事呢?我淫荡的笑,哈哈.........................

那么第二种JMP hook 技术就出来。

首先和第一种一样都有一个g_StubCode,而这个大小已经变大了,不再是5Byte, 为什么呢,就是hook 完毕之后,就不但算在恢复原来函数的入口的字节了,等跳转到我们自己函数中,做完要做的事情,然后通过stubCode 在返回原来的函数,这样说明了一个问题,就是这样hook 只能在原来函数之前做事,之后,就不能做事了,只能直接返回,当然有没有可能还能做事情呢,能,怎么办,有点复杂。 这里我们先讨论,简单这种。

1. 替换原来函数如果处若干个字节,但是必须大于要放JMP指令字节数,那么这个若干是多少呢?因为相对地址JMP,是5个字节,原函数入口处5个字节正好是完整指令,那么没有问题,如果5个自己不是完整指令,那么这样将一个指令劈成两半的做法是不是很不道德呢。所以这里应该使用反汇编的手法识别出完成指令,且这些指令的长度是大于JMP指令长度的最小值。

2. 将识别出的完整指令备份到stubCode, 然后将原来函数入口处写入JMP指令。

3. stubcode 尾部也加入JMP指令,这个JmP指令跳转到原来函数备份到stubCode 中的下一条指令,这样就可以无缝连接在一起了,哈哈,我淫荡的笑啊,我淫荡的笑。。。。。

4. 在新函数的结束的地方将stubCode 强转为该类型的函数指针,调用返回。

上代码:

[cpp] view plaincopy
  1. #include "sethook.h"  
  2. #include "disasm.h"  
  3. BOOL InstallHook (HOOK_DATA *pHook)   
  4. {  
  5.     ULONG dwJmpDst = 0;  
  6.     int nInstSize = 0;  
  7.     int nInstSizeIfMoved = 0;  
  8.     int nLoop = 0;  
  9.     BOOLEAN bRetCode = FALSE;     
  10.   
  11.     pHook->nHeadSize = 0;  
  12.     pHook->nStubSize = 5; // == sizeof(JMP rel32)  
  13.   
  14.     while (pHook->nHeadSize < 5) // need at least 5 char to path  
  15.     {  
  16.         bRetCode = Dasm_GetInstructionSize(pHook->pTargetFunc + pHook->nHeadSize, &nInstSize, &nInstSizeIfMoved);  
  17.         if (bRetCode == FALSE)  
  18.         {  
  19.             return FALSE;  
  20.         }  
  21.   
  22.         pHook->nHeadSize += nInstSize;  
  23.         pHook->nStubSize += nInstSizeIfMoved;  
  24.   
  25.         if (pHook->nStubSize >= MAX_STUB_SIZE)   
  26.         {  
  27.             return FALSE;  
  28.         }        
  29.     }  
  30.   
  31.     memcpy(pHook->pHead, pHook->pTargetFunc, pHook->nHeadSize);  
  32.   
  33.     // create stub  
  34.     bRetCode = Dasm_MoveProgram(pHook->pTargetFunc, pHook->nHeadSize, pHook->pStub, &pHook->nStubSize, MAX_STUB_SIZE);  
  35.     if (bRetCode == FALSE)  
  36.     {  
  37.         return FALSE;  
  38.     }  
  39.   
  40.     if ( (UCHAR)pHook->pTargetFunc[0] == 0xE9)  
  41.     {  
  42.         ULONG ulDstAddr = (ULONG)pHook->pTargetFunc + 5 + *(PULONG)(pHook->pTargetFunc + 1);  
  43.         ULONG ulSrcAddr = (ULONG)pHook->pStub + 5;  
  44.         dwJmpDst = ulDstAddr - ulSrcAddr;         
  45.         pHook->pStub[0] = 0xE9;  
  46.         memcpy(&pHook->pStub[1], &dwJmpDst, 4);  
  47.     }  
  48.   
  49.     // add jmp at the end of the stub  
  50.     dwJmpDst = (ULONG)pHook->pTargetFunc - (ULONG)pHook->pStub - pHook->nStubSize;  
  51.     pHook->pStub[pHook->nStubSize] = 0xE9; // JMP rel32  
  52.     memcpy(&pHook->pStub[pHook->nStubSize+1], &dwJmpDst, 4);  
  53.     pHook->nStubSize += 5;  
  54.   
  55.     DWORD dwOldProtect = 0;  
  56.     VirtualProtect(pHook->pTargetFunc, pHook->nHeadSize, PAGE_EXECUTE_READWRITE, &dwOldProtect);  
  57.     pHook->pTargetFunc[0] = 0xE9; // JMP rel32  
  58.     dwJmpDst = (ULONG)pHook->pHookFunc - (ULONG)pHook->pTargetFunc - 5; // 5==sizeof(JMP rel32)  
  59.     memcpy(pHook->pTargetFunc + 1, &dwJmpDst, 4);  
  60.   
  61.     for (nLoop = 5; nLoop < pHook->nHeadSize; nLoop ++)  
  62.         pHook->pTargetFunc[nLoop] = 0x90; // NOP  
  63.   
  64.   
  65.   
  66.     return TRUE;  
  67. }  

[cpp] view plaincopy
  1. int  WINAPI MyMessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uiType)  
  2. {  
  3.   
  4.     return ((pMessageBoxW)&g_pData->pStub)(  
  5.         hWnd,  
  6.         lpText,  
  7.         lpCaption,  
  8.         uiType);  
  9.   
  10.     return 0;  
  11. }  
  12.   
  13. BOOL WINAPI MyCreateProcessW( __in_opt LPCWSTR lpApplicationName,  
  14.                       __inout_opt LPWSTR lpCommandLine,  
  15.                       __in_opt LPSECURITY_ATTRIBUTES lpProcessAttributes,  
  16.                       __in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes,  
  17.                       __in BOOL bInheritHandles,  
  18.                       __in DWORD dwCreationFlags,  
  19.                       __in_opt LPVOID lpEnvironment,  
  20.                       __in_opt LPCWSTR lpCurrentDirectory,  
  21.                       __in LPSTARTUPINFOW lpStartupInfo,  
  22.                       __out LPPROCESS_INFORMATION lpProcessInformation )  
  23. {  
  24.     return ((PCreateProcessW)(&g_pData->pStub))( lpApplicationName,  
  25.         lpCommandLine,  
  26.         lpProcessAttributes,  
  27.         lpThreadAttributes,  
  28.         bInheritHandles,  
  29.         dwCreationFlags,  
  30.         lpEnvironment,  
  31.         lpCurrentDirectory,  
  32.         lpStartupInfo,  
  33.         lpProcessInformation);  
  34. }  
  35.   
  36.   
  37.   
  38. int _tmain(int argc, _TCHAR* argv[])  
  39. {  
  40.   
  41.     PCreateProcessW pOrigMBAddress = (PCreateProcessW)GetProcAddress( GetModuleHandle(_T("kernelbase.dll")), "CreateProcessW" );  
  42.       
  43.     //BeginRedirect(MyMessageBoxW);  
  44.   
  45.     HOOK_DATA *pData = new HOOK_DATA;  
  46.     g_pData = pData;  
  47.     pData->pTargetFunc = (PUCHAR)pOrigMBAddress;  
  48.     pData->pHookFunc = (PUCHAR)MyCreateProcessW;  
  49.   
  50.     //InstallHook( pData );  
  51.   
  52.     TCHAR szProcessNAme[60] = L"notepad.exe";  
  53.       
  54.     STARTUPINFOW info = { sizeof(info)};  
  55.     PROCESS_INFORMATION Pi = {0};  
  56.     CreateProcessW( NULL,  
  57.         szProcessNAme,  
  58.         NULL,  
  59.         NULL,  
  60.         FALSE,  
  61.         0,  
  62.         NULL,  
  63.         NULL,  
  64.         &info,  
  65.         &Pi);  
  66.   
  67.     return 0;  
  68. }  

可以明显的看到,这样hook 的优势所在. API hook 基本就是这样了,JMP short 的这种怎么实现的,一般我们要看看windows API 的函数汇编,


0x74cd43AB CreateProcessW 的地址,这个函数进来的第一件事,就是mov edi, edi 这条指令看起来很蛋疼,但是这个蛋疼的指令,却有着很牛叉的意义,另外MS的所有API都是这样的,这个是一个公开的秘密,在这里放一个JmP short , 那么这个JMP short 跳转的是-127 ~ 127 那么我们怎么可能将函数放到MS API的位置呢,看到函数上面的5nop 了,

这个5个字节,怎么这么适合放相对地址JMP far,  我淫荡的笑啊.... 剩下的和我们刚才的一样啦,这个技术就是MS在放补丁时候,hot fix 就是这样的。MS都在用,你就放心的用吧,要死大家一起死。

API hook 在本进程中,无非就是兀自手淫,没有什么大意义,那么如何才能将API hook 放到别人的进程中,我们这里讨论两种技术,第一种CreateRomoteThread, 第二种Windows hook,.

1 CreateRomoteThread 

用法很简单,

1. 准备一个dll, DllMain中的 DLL_PROCESS_ATTACH 中,进行sethook 调用,那么就是可以hook 本进程了。

2. 将这个dll 放到要hook 的进程中去

[cpp] view plaincopy
  1. void Inject(LPCTSTR lpszDllPath, DWORD dwProcessID )  
  2. {  
  3.     HANDLE hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, dwProcessID );  
  4.     if ( hProcess != NULL )  
  5.     {  
  6.         LPVOID pAddr = VirtualAllocEx(  
  7.             hProcess,  
  8.             NULL,   
  9.             (_tcslen(lpszDllPath) + 1) * sizeof(*lpszDllPath),   
  10.             MEM_COMMIT | MEM_RESERVE,  
  11.             PAGE_EXECUTE_READWRITE);  
  12.   
  13.         if ( pAddr != NULL )  
  14.         {  
  15.             SIZE_T uSize = 0;  
  16.             WriteProcessMemory(  
  17.                 hProcess,  
  18.                 pAddr,  
  19.                 lpszDllPath,  
  20.                 (_tcslen(lpszDllPath) + 1) * sizeof(*lpszDllPath),  
  21.                 &uSize);  
  22.   
  23.            HANDLE hThread = CreateRemoteThread( hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibraryW, pAddr, 0, 0 );  
  24.            CloseHandle( hThread );  
  25.         }  
  26.     }  
  27. }  

但是常常因为你没有足够的权限而失败,那么Windows hook 技术就可以帮助到我们了。

Windows hook 技术

大家可以看看http://en.wikipedia.org/wiki/Hooking  对hook 的一种通用解释,

Windows 上主要使用SetWindowsHookEx 或者不带EX的函数,首先我们看看

HHOOK WINAPI SetWindowsHookEx(

  __in  int idHook,

  __in  HOOKPROC lpfn,

  __in  HINSTANCE hMod,

  __in  DWORD dwThreadId

);

这个API的用法,idHook 表示你要hook 那种事件,如键盘,消息,等,

Lpfn 表示hook 处理函数

Hmod 模块句柄,这个就是dll 的句柄

dwThreadId 表示要hook 那个线程的,如果是0 the hook procedure is associated with all existing threads running in the same desktop as the calling thread.  就是说当前桌面的所有线程都会加载到这个dll. 厉害了吧,我淫荡的笑。。。。。。。。。。。。。

SetWindowsHookEx 一般成功了返回了hook 的句柄,但是这个句柄要在hook 的处理函数中使用,我们不是说,将hook 处理函数写在dll 中,那么dll 的在A进程调用了SetWindowsHookEx ,那么dll 映射到B进程中,可是hook 句柄在A进程中,那么B中的hook 处理函数,如何才能使用这个hook 句柄呢?当然进程通信的方式很多,这里使用一种比较简单的策略,就是dll 中增加一个共享数据段。这个共享数据段,就是当dll 映射到不同进程中,每个进程都可以看到这里数据,而且其中一个进程改变这个区域的数据,其他进程也就看到了。


[cpp] view plaincopy
  1. #pragma comment(linker, "/SECTION:.myseg,RWS")   
  2. #pragma data_seg (".myseg")  
  3. HWND g_Hwnd = NULL;  
  4. HHOOK g_HookHandle = NULL;  
  5. #pragma data_seg()  

做个简单解释,dll 中在不同进程可能相同的内容,映射的地址不同,因为不同进程空间,各自不能直接地址访问,dll 的可读部分,进程中其实大家共享一份,只是映射的在virtual address 不同而已,但是将A进程要去写dll 中的数据,那么对不起,这个时候,OS就会重新分一块空间给A就写,A写进去的,就是A的,不会影响到B进程,但是共享数据段,就是所有进程的将使用同一份物理内存,虽然这个空间在不同进程中的地址不同,但是却是同一块物理内存,这样A写了进去,B就可以看到。

哈哈, 有了这样的技术,大家想干什么就干什么了, 你说是不是呢

http://download.csdn.net/detail/zwh37333/4225393

http://download.csdn.net/detail/zwh37333/4225395

上面的位置,是源码。

原创粉丝点击