原理
API函数保存在操作系统的DLL文件中,在运行程序后程序会将API所在的DLL加载入进程中,当在程序中使用某个API函数时。这样程序就会像调用自己的函数一样调用API,大体过程如图1所示。

图1 调用API函数的大体过程
从图1中可以看出,在进程中当EXE模块调用CreateFile()函数的时候,会去调用kernel32.dll模块中的CreateFile()函数,因为真正的CreateFile()函数的实现在kernel32.dll模块中。
CreateFile()是API函数,API函数也是由人编写的代码再编译而成的,也有其对应的二进制代码。既然是代码,那么就可以被修改。通过一种“野蛮”的方法来直接修改API函数在内存中的映像,从而对API函数进行HOOK。使用的方法是,直接使用汇编指令的jmp指令将其代码执行流程改变,进而执行我们的代码,这样就使原来的函数的流程改变了。执行完我们的流程以后,可以选择性地执行原来的函数,也可以不继续执行原来的函数。
假设要对某进程的kernel32.dll的CreateFile()函数进行HOOK,首先需要在指定进程中的内存中找到CreateFile()函数的地址,然后修改CreateFile()函数的首地址的代码为jmpMyProc的指令。这样,当指定的进程调用CreateFile()函数时,就会首先跳转到我们的函数当中去执行流程,这样就完成了我们的HOOK了。

由于这种方法是在程序流程中直接进行嵌入jmp指令来改变流程的,所以就把它叫做Inline Hook。
实现
Inline  Hook是在程序中嵌入了jmp指令然后跳转到我们的程序流程中继续执行的,jmp指令的用法是jmp  目的地址,减5是因为jmp xxxx指令机器码长5个字节
jmp后的偏移量 =  目的地址  -   源地址   -  5
目的地址:钩子函数地址  源地址:要HOOK的函数地址
梳理一下我们内联钩子的流程:
- 构造jmp指令
 
- 在内存中找到想要HOOK的函数地址,并保存为HOOK位置处的前5个字节
 
- 将构造的跳转指令写入需要HOOK的位置
 
- 当HOOK位置被执行时会跳转到我们的执行流程
 
- 如果要继续原来的流程,那么取消HOOK,也就是还原被修改的字节
 
- 执行原来的流程
 
- 继续HOOK住原来的位置
 
代码
先封装一个CILHook类,
头文件CILHook.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
   | #pragma once #include <Windows.h> #include <tchar.h> class CILHook { public:     CILHook();     ~CILHook();
      BOOL Hook(LPCTSTR pszModuleName, LPCSTR pszFuncName, PROC pfnHookFunc);     void UnHook();     BOOL ReHook();
  private:     FARPROC m_pfnOrig;     BYTE m_bOldBytes[5];     BYTE m_bNewBytes[5];
  };
 
 
 
  | 
 
CILHook.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
   | #include "CILHook.h"
  CILHook::CILHook() {     m_pfnOrig = NULL;     ZeroMemory(m_bNewBytes, 5);     ZeroMemory(m_bOldBytes, 5);
  }
  CILHook::~CILHook() {     UnHook(); }
 
 
  BOOL CILHook::Hook(LPCWSTR pszModuleName, LPCSTR pszFuncName, PROC pfnHookFunc) {     BOOL bRet = FALSE;          m_pfnOrig = GetProcAddress(GetModuleHandle(pszModuleName), pszFuncName);     if (m_pfnOrig != NULL) {                  DWORD dwNum = 0;         ReadProcessMemory(GetCurrentProcess(), (LPCVOID)m_pfnOrig, m_bOldBytes, 5, &dwNum);
          m_bNewBytes[0] = '\xe9';                  *(DWORD*)(m_bNewBytes + 1) = (DWORD)pfnHookFunc - (DWORD)m_pfnOrig - 5;
          WriteProcessMemory(GetCurrentProcess(), (LPVOID)m_pfnOrig, m_bNewBytes, 5, &dwNum);         bRet = TRUE;     }     return bRet; }
 
  void CILHook::UnHook() {     BOOL bRet = FALSE;     if (!m_pfnOrig != 0) {         DWORD dwNum = 0;         WriteProcessMemory(GetCurrentProcess(), m_pfnOrig, m_bNewBytes, 5, &dwNum);         bRet = TRUE;     }      } BOOL CILHook::ReHook() {     BOOL bRet = FALSE;     if (m_pfnOrig != 0) {         DWORD dwNum = 0;         WriteProcessMemory(GetCurrentProcess(), (LPVOID)m_pfnOrig, m_bNewBytes, 5, &dwNum);         bRet = TRUE;     }     return bRet; }
 
  | 
 
主程序代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
   | #include <iostream> #include "CILHook.h"
  CILHook MsgHook;
 
  int WINAPI MyMessageBoxW(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption,UINT uType)  { 	MsgHook.UnHook(); 	MessageBox(NULL, _T("Hook"), lpCaption, uType); 	MsgHook.ReHook(); 	return 0; }
  int main() { 	 	MsgHook.Hook(_T("User32.dll"), "MessageBoxA", (FARPROC)MyMessageBoxW); 	MessageBox(NULL, _T("test"), _T("test"), MB_OK); 	MsgHook.UnHook(); 	return 0; }
 
  | 
 
利用过程
在windows 10中测试失败,仍然执行正常的MessageBox函数,原因是windows 10操作系统中采用ASLR(地址随机化),用公式计算出来的偏移地址是无效的,所以无法jmp到钩子函数执行。而且在多线程系统中,可能有某个线程就在这个时候调用了修改的系统函数,造成无法预期的结果。在windows 10中,可以使用微软的一个轻量级的开源库——detours来完成Hook。

使用detours进行API Hook
这里可以使用微软的一个轻量级的开源库来完成Hook。Detours是微软提供的一个开发库,使用它可以简单、高效、稳定地实现API HOOK的功能。
安装
在Visual Studio中选择工具->NuGet包管理器->程序包管理器控制台
输入安装命令会自动安装Detours库:
导入代码如下:
1 2
   | #include <detours.h> #pragma comment (lib,"detours.lib")
 
  | 
 
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
   | #include <Windows.h> #include <detours.h> #pragma comment (lib,"detours.lib") static int (WINAPI* OldMesssageBoxA) (     HWND hWnd,     LPCSTR lpText,     LPCSTR lpCaption,     UINT uType     ) = MessageBoxA;
  int WINAPI MyFunction0(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) {     return OldMesssageBoxA(NULL, "Hooked", "Warning", MB_OKCANCEL); }
  int main() {     DetourTransactionBegin();     DetourUpdateThread(GetCurrentThread());     DetourAttach(&(PVOID&)OldMesssageBoxA, MyFunction0);     DetourTransactionCommit();
      MessageBox(NULL, _T("test"), _T("test"), MB_OK);
 
      return 0; }
 
  | 
 
利用过程
成功

具体内容和利用可见
渗透技巧——从远程桌面客户端提取明文凭据
Detours InLine Hook