/// <summary>
 /// 对CAPIHook进行了dotnet下的封装。
 ///     其中fnHook: 为挂钩的函数的新地址的委托
 ///     不要传送和挂钩的函数不一致的委托,关于如果从程序上阻止这个行为,暂时还没有想到好的办法。
 //////////////////////////////////////////////////////////////////////////////////////////////////
 /// 在dotnet中使用APIHook时发现了以下问题:
 /// 我们在c#中一般调用API是采用以下形式:
 ///                [DllImport("user32", EntryPoint = "MessageBoxA")]
 ///         public static extern int MessageBox(IntPtr hwnd, string lpText, string lpCaption, uint wType);
 /// 但是我发现
 ///       1.在我们还没有使用APIHook来Hook MessageBox时,如果调用了MessageBox,即使以后使用APIHook来Hook了,这个MessageBox函数
 ///         仍然弹出正常的MessageBox,而不是我们希望的Hook MessageBox.
 ///       2.如果先调用APIHook进行了Hook,然后调用MessageBox,那么会显示hook messagebox.这时如果对APIHook类进行Unhook,再显示
 ///         messageBox还是会进入hook messagebox的函数,并没有回到normal messagebox.
 /// 针对这个情况,我推论出dotnet在invoke api时,一定采取了缓存机制,将地址保存起来了.
 /// 那么它将这一地址保存在什么地方呢?是全局中还是只是本类中呢?
 ///              
 /// 我又进行了一次测试:
 ///     我定义了一个临时类,该类只有一个函数就是public static extern int MessageBox,
 /// 测试结果:
 ///     1.如果调用Form的MessageBox,弹出normal messagebox.然后再使用APIHook进行hook,Form的messagebox仍然显示
 ///       normal messagebox.但是myclass的messagebox已经显示hooked messagebox了.
 ///     2.unhook后不改变上面的行为.
 /// 结论:
 ///  public static extern int MessageBox(IntPtr hwnd, string lpText, string lpCaption, uint wType);
 ///    是作为类的成员保存了起来.它具有和静态成员类似的特性.它使用时才获得native地址,并将这个地址保存在
 ///    本类中,它是作为类的静态成员存在的.
 //////////////////////////////////////////////////////////////////////////
 /// 另外对不熟悉钩子的人来讲,有一点需要说明:
 ///    本类只是通过修改IAT实现了hook api,通过本类可以截获某个进程调用了的指定api(windows api或者动态链接库中的api)
 ///    本类并没有实现注入进程空间.如果需要使用hook api修改别的进程的行为,需要先注入进程空间,然后使用hook api才有效.
 /////////////////////////////////////////////////////////////////////////
 /// 使用本类可以直接用APIHook()然后调用Hook,或者使用带参数的APIHook构造函数.
 /// unhook可以不用显式调用,系统退出时会自动释放.
 /// </summary>
// Hob.Toolbox.APIHook.h
#pragma once
#include "APIHook.h"
using namespace System;
using namespace System::Runtime::InteropServices;
using namespace System::Diagnostics;
namespace Hob{namespace Toolbox{namespace Classes
{
  public ref class APIHook
 {
 public:
  /// <summary>
  /// 构造函数
  /// </summary>
  APIHook()
  {
   //生成一个CAPIHook对象
   m_apiHookPtr=new CAPIHook();
  }
  APIHook(String^ CalleeModName,String^ FuncName,Delegate^ fnHook,bool fExcludeAPIHookMod)
  {
   //生成一个CAPIHook对象
   m_apiHookPtr=new CAPIHook();
   //hook
   Hook(CalleeModName,FuncName,fnHook,fExcludeAPIHookMod);
  }
  APIHook(String^ CalleeModName,String^ FuncName,Delegate^ fnHook)
  {
   //生成一个CAPIHook对象
   m_apiHookPtr=new CAPIHook();
   //hook
   Hook(CalleeModName,FuncName,fnHook,true);
  }
  /// <summary>
  /// 挂钩某个API
  /// CalleeModName:希望挂钩的模块的名字
  /// FuncName:希望挂钩的模块中的函数的名字
  /// fnHook:挂钩的函数的新地址  
  /// fExcludeAPIHookMod:建立挂钩时是否包括本类所处的模块 true:不包括 false:包括
  /// <summary>
  void Hook(String^ CalleeModName,String^ FuncName,Delegate^ fnHook,bool fExcludeAPIHookMod)
  {
   //如果已经挂钩,必须先UnHook
   if(BeHook) UnHook();
   //开始挂钩
   PSTR pszCalleeModName   = (PSTR)Marshal::StringToHGlobalAnsi(CalleeModName).ToPointer();
   PSTR pszFuncName   = (PSTR)Marshal::StringToHGlobalAnsi(FuncName).ToPointer();
   PROC pfnHook            =(PROC) Marshal::GetFunctionPointerForDelegate(fnHook).ToPointer();
   m_delegatefnHook     = fnHook;//save delegate防止delegate被垃圾回收
   m_fnHookType         = fnHook->GetType();
   m_apiHookPtr->Hook(pszCalleeModName,pszFuncName,pfnHook,fExcludeAPIHookMod);
  }
  void Hook(String^ CalleeModName,String^ FuncName,Delegate^ fnHook)
  {
   Hook(CalleeModName,FuncName,fnHook,true);
  }
  //撤销挂钩
  void UnHook()
  {
   if(!BeHook) return;//没有hook
   m_apiHookPtr->UnHook();
  }
  //析构函数
  ~APIHook()
  {
   //释放托管资源.....
   //调用释放非托管资源
   this->!APIHook();
  }
  //finallize
  !APIHook()
  {
   //释放非托管资源
   UnHook();
   delete m_apiHookPtr;
  }
  //得到原来函数api的委托
  property Delegate^ RawAPI
  {
   Delegate^ get()
   {
    try
    {
     if(!BeHook) return nullptr;
     PROC pfnOrig=(PROC)(*m_apiHookPtr);
     Delegate^ d=Marshal::GetDelegateForFunctionPointer(IntPtr((PVOID)pfnOrig),m_fnHookType);
     return d;
    }
    catch (Exception^)
    {
     return nullptr;
    }
   }
  }
  //是否被hook
  property bool BeHook
  {
   bool get()
   {
    return m_apiHookPtr->HasHook();
   }
  }
 private:
  CAPIHook* m_apiHookPtr;//CAPIHook对象
  Delegate^ m_delegatefnHook; //保存委托,避免被释放掉,造成地址不可用
  Type^ m_fnHookType;//挂钩的新函数的委托类型
 };
}}}
//APIHook.h
#pragma once
/// <summary>
/// CAPIHook类 本类用c++写成,可以直接在c++下用.
/// 在此对 Jeffrey Richter 表示感谢
/// 实现应该放在cpp中,并且用#pragma unmanaged标示
/// 直接写在头文件会被认为是托管的代码,从而会发生一些异常(比如托管的异常:LoadLocker和Reentrancy)
/// 注意:  
///   最好不要使用此类挂钩 API的LoadLibraryA LoadLibraryW LoadLibraryExA LoadLibraryExW GetProcAddress
///                             因为本dll已经挂钩了这几个函数了。
///   最好不要使用多个对象挂钩同一个API,没有具体测试会有什么后果。
/// </summary>
class CAPIHook
{
public:
 /// <summary>
 /// 构造函数
 /// </summary>
 CAPIHook(void);
 CAPIHook(PSTR pszCalleeModName,PSTR pszFuncName, PROC pfnHook,bool fExcludeAPIHookMod);
 //析构函数
 ~CAPIHook(void);
 //返回被hook函数原来的地址
 operator PROC();
 /// <summary>
 /// 挂钩某个API
 /// pszCalleeModName:希望挂钩的模块的名字
 /// pszFuncName:希望挂钩的模块中的函数的名字
 /// fnHook:挂钩的函数的新地址  
 /// fExcludeAPIHookMod:建立挂钩时是否包括本类所处的模块 true:不包括 false:包括
 /// <summary>
 void Hook(PSTR pszCalleeModName,PSTR pszFuncName, PROC pfnHook,bool fExcludeAPIHookMod);
 //撤销挂钩
 void UnHook();
 //是否挂钩了
 bool HasHook();
private:
 bool m_hasHook;//是否已经挂钩
 static PVOID sm_pvMaxAppAddr;// 最大私有内存空间
 static CAPIHook* sm_pHead;    //APIHook对象链表的头部
 CAPIHook* m_pNext;            //本APIHook对象的下一个对象
 PCSTR m_pszCalleeModName;     //希望挂钩的模块的名字
 PCSTR m_pszFuncName;          // 希望挂钩的模块中的函数的名字
 PROC  m_pfnOrig;              // 被挂钩的函数原来的地址
 PROC  m_pfnHook;              // 挂钩的函数的新地址
 bool  m_fExcludeAPIHookMod;   // 建立挂钩时是否包括本类所处的模块 true:不包括 false:包括
private:
 //替换所有模块中的函数地址为新地址
 static void ReplaceIATEntryInAllMods(PCSTR pszCalleeModName,PROC pfnCurrent, PROC pfnNew, bool fExcludeAPIHookMod);
 //替换某个模块中的函数地址为新地址
 static void ReplaceIATEntryInOneMod(PCSTR pszCalleeModName,PROC pfnCurrent, PROC pfnNew, HMODULE hmodCaller);
 //调用真实的GetProcAddress获得函数的原来的真正地址
 static FARPROC WINAPI GetProcAddressRaw(HMODULE hmod,LPCSTR pszProcName);
 // 得到包含指定内存地址的模块
 static HMODULE ModuleFromAddress(PVOID pv);
private:
 #pragma region API被动态调用时的处理
 //下面处理LoadLibrary 和 GetProcAddress
 // 当我们使用这些函数动态调用API时,本类无法正确截获
 static CAPIHook sm_LoadLibraryA;
 static CAPIHook sm_LoadLibraryW;
 static CAPIHook sm_LoadLibraryExA;
 static CAPIHook sm_LoadLibraryExW;
 static CAPIHook sm_GetProcAddress;
 //新的LoadLibrary的函数
 static void FixupNewlyLoadedModule(HMODULE hmod, DWORD dwFlags);
 //新的LoadLibraryA
 static HMODULE WINAPI LoadLibraryA(PCSTR pszModulePath);
 //新的LoadLibraryW
 static HMODULE WINAPI LoadLibraryW(PCWSTR pszModulePath);
 //新的LoadLibraryExA
 static HMODULE WINAPI LoadLibraryExA(PCSTR pszModulePath,HANDLE hFile, DWORD dwFlags);
 //新的LoadLibraryExW
 static HMODULE WINAPI LoadLibraryExW(PCWSTR pszModulePath,HANDLE hFile, DWORD dwFlags);
 //新的GetProcAddress
 static FARPROC WINAPI GetProcAddress(HMODULE hmod, PCSTR pszProcName);
 #pragma endregion
};
 
//APIHook.cpp
#include "Stdafx.h"
#include "APIHook.h"
#include "MyASSERT.h"
#include <ImageHlp.h>//由于ImageHlp.h中有部分定义和system命名空间下的混淆,应该在system前include
#pragma comment(lib, "ImageHlp")
#include <Tlhelp32.h>
#pragma unmanaged //以下为非托管代码
PVOID CAPIHook::sm_pvMaxAppAddr=NULL;// 最大私有内存空间
const BYTE cPushOpCode = 0x68;   //x86平台下的PUSH的操作码
CAPIHook* CAPIHook::sm_pHead=NULL;
//构造函数
CAPIHook::CAPIHook()
{
 m_hasHook=false;
 m_pfnOrig=NULL;
 m_pfnHook=NULL;
}
//构造函数
CAPIHook::CAPIHook(PSTR pszCalleeModName,PSTR pszFuncName, PROC pfnHook,bool fExcludeAPIHookMod)
{
 m_hasHook=false;
 m_pfnOrig=NULL;
 m_pfnHook=NULL;
 Hook(pszCalleeModName,pszFuncName,pfnHook,fExcludeAPIHookMod);
}
//析构函数
CAPIHook::~CAPIHook(void)
{
 UnHook();
}
//返回被hook函数原来的地址
CAPIHook::operator PROC()
{
 return(m_pfnOrig);
}
bool CAPIHook::HasHook()
{
 return m_hasHook;
}
/// <summary>
/// 挂钩某个API
/// pszCalleeModName:希望挂钩的模块的名字
/// pszFuncName:希望挂钩的模块中的函数的名字
/// fnHook:挂钩的函数的新地址  
/// fExcludeAPIHookMod:建立挂钩时是否包括本类所处的模块 true:不包括 false:包括
/// <summary>
void CAPIHook::Hook(PSTR pszCalleeModName,PSTR pszFuncName, PROC pfnHook,bool fExcludeAPIHookMod)
{
 //如果已经挂钩,必须先UnHook
 if(m_hasHook) UnHook();
 if (sm_pvMaxAppAddr == 0)
 {
  //lpMaximumApplicationAddress以上的函数地址需要进行特殊处理(仅对windows98而言)
  SYSTEM_INFO si;
  GetSystemInfo(&si);
  sm_pvMaxAppAddr = si.lpMaximumApplicationAddress;
 }
 //修改 APIHook 对象的链表
 m_pNext  = sm_pHead;   
 sm_pHead = this;       
 //保存挂钩的函数的信息
 m_pszCalleeModName   = pszCalleeModName;
 m_pszFuncName        = pszFuncName;
 m_pfnHook            = pfnHook;
 m_fExcludeAPIHookMod = fExcludeAPIHookMod;
 m_pfnOrig            = GetProcAddressRaw(GetModuleHandleA(m_pszCalleeModName), m_pszFuncName);
 chASSERT(m_pfnOrig != NULL);  //模块中没有找到对应函数
 if (m_pfnOrig > sm_pvMaxAppAddr)
 {
  // 地址在共享dll中,地址需要修正(仅对windows98而言)
  PBYTE pb = (PBYTE) m_pfnOrig;
  if (pb[0] == cPushOpCode)
  {
   // 跳过PUSH操作代码得到真正的地址
   LPVOID pv = * (LPVOID*) &pb[1];
   m_pfnOrig = (PROC) pv;
  }
 }
 // 在当前已经加载的所有模块中挂钩函数
 ReplaceIATEntryInAllMods(m_pszCalleeModName, m_pfnOrig, m_pfnHook, m_fExcludeAPIHookMod);
 m_hasHook= (m_pfnOrig != NULL);//ok
}
//撤销挂钩
void CAPIHook::UnHook()
{
 if(!m_hasHook) return;//没有hook
 //停止挂钩函数,替换为原来的函数
 ReplaceIATEntryInAllMods(m_pszCalleeModName, m_pfnHook, m_pfnOrig, m_fExcludeAPIHookMod);
 // 将对象从链表中移出
 CAPIHook* p = sm_pHead;
 if (p == this)
 {     // Removing the head node
  sm_pHead = p->m_pNext;
 }
 else
 {
  BOOL fFound = FALSE;
  // Walk list from head and fix pointers
  for (; !fFound && (p->m_pNext != NULL); p = p->m_pNext)
  {
   if (p->m_pNext == this)
   {
    // Make the node that points to us point to the our next node
    p->m_pNext = p->m_pNext->m_pNext;
    fFound=TRUE;
    break;
   }
  }
  chASSERT(fFound==TRUE);
 }
 m_hasHook=false;
}
//替换所有模块中的函数地址为新地址
void CAPIHook::ReplaceIATEntryInAllMods(PCSTR pszCalleeModName,PROC pfnCurrent, PROC pfnNew, bool fExcludeAPIHookMod)
{
 HMODULE hmodThisMod = fExcludeAPIHookMod ? ModuleFromAddress(&CAPIHook::ReplaceIATEntryInAllMods) : NULL;
 //得到当前进行的模块列表
 HANDLE hSnapshot=CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,GetCurrentProcessId());//打开
 MODULEENTRY32 me = { sizeof(me) };
 for (BOOL fOk = Module32First(hSnapshot,&me); fOk; fOk = Module32Next(hSnapshot,&me))
 {
  // NOTE: We don't hook functions in our own module
  if (me.hModule != hmodThisMod)
  {
   // Hook this function in this module
   ReplaceIATEntryInOneMod(pszCalleeModName, pfnCurrent, pfnNew, me.hModule);
  }
 }   
 CloseHandle(hSnapshot);//关闭      
}
//替换某个模块中的函数地址为新地址
void CAPIHook::ReplaceIATEntryInOneMod(PCSTR pszCalleeModName,PROC pfnCurrent, PROC pfnNew, HMODULE hmodCaller)
{
 //得到模块(hmodCaller)引入片断(import section)的地址
 ULONG ulSize;
 PIMAGE_IMPORT_DESCRIPTOR pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)
  ImageDirectoryEntryToData(hmodCaller, TRUE,IMAGE_DIRECTORY_ENTRY_IMPORT, &ulSize);
 // 模块(hmodCaller)没有引入片断(import section)
 if (pImportDesc == NULL) return;
 // 查找模块(hmodCaller)合适的引入描述符,该描述符描述的是模块(hmodCaller)使用被调用的模块(pszCalleeModName)的函数信息
 for (; pImportDesc->Name; pImportDesc++)
 {
  PSTR pszModName = (PSTR) ((PBYTE) hmodCaller + pImportDesc->Name);
  if (lstrcmpiA(pszModName, pszCalleeModName) == 0)
   break;   // Found
 }
 // 模块(hmodCaller)中没有关于被调用的模块(pszCalleeModName)的函数信息
 if (pImportDesc->Name == 0) return;
 // 得到调用者hmodCaller 使用被调用的模块(pszCalleeModName) 的IAT(引入地址表)
 PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)((PBYTE) hmodCaller + pImportDesc->FirstThunk);
 //替换当前的函数地址为需要的新地址
 for (; pThunk->u1.Function; pThunk++)
 {
  //得到需要被替换的函数地址
  PROC* ppfn = (PROC*) &pThunk->u1.Function;
  // 这是我们要找的函数吗?
  BOOL fFound = (*ppfn == pfnCurrent);
  if (!fFound && (*ppfn > sm_pvMaxAppAddr)) //如果没有找到,修正
  {
   //如果我们运行在win98下,地址在共享dll中,这个可能不是函数地址
   //这种情况下,指令操作使用的地址应该是正确的地址
   PBYTE pbInFunc = (PBYTE) *ppfn;
   if (pbInFunc[0] == cPushOpCode)
   {
    // 我们找到了PUSH操作,真实地址就在后面跟着
    ppfn = (PROC*) &pbInFunc[1];
    // 这是我们要找的函数吗?
    fFound = (*ppfn == pfnCurrent);
   }
  }
  if (fFound)
  {
   // 获得了匹配的地址,我们改写它(IAT)
   WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew, sizeof(pfnNew), NULL);
   return;  // 成功,退出
  }
 }
 // If we get to here, the function is not in the caller's import section
}
//调用真实的GetProcAddress获得函数的原来的真正地址
FARPROC WINAPI CAPIHook::GetProcAddressRaw(HMODULE hmod,LPCSTR pszProcName)
{
 return ::GetProcAddress(hmod,pszProcName);
}
// 得到包含指定内存地址的模块
HMODULE CAPIHook::ModuleFromAddress(PVOID pv)
{
 MEMORY_BASIC_INFORMATION mbi;
 return( (VirtualQuery(pv, &mbi, sizeof(mbi)) != 0) ?(HMODULE) mbi.AllocationBase : NULL);
}
//以下fExcludeAPIHookMod must be true,否则下面的新函数(LoadLibraryA 等等)将会死循环。也即本模块的IAT地址不能替换.
CAPIHook CAPIHook::sm_LoadLibraryA("Kernel32.dll", "LoadLibraryA",(PROC) CAPIHook::LoadLibraryA, true);
CAPIHook CAPIHook::sm_LoadLibraryW("Kernel32.dll", "LoadLibraryW",(PROC) CAPIHook::LoadLibraryW, true);
CAPIHook CAPIHook::sm_LoadLibraryExA("Kernel32.dll", "LoadLibraryExA",(PROC) CAPIHook::LoadLibraryExA, true);
CAPIHook CAPIHook::sm_LoadLibraryExW("Kernel32.dll", "LoadLibraryExW",(PROC) CAPIHook::LoadLibraryExW, true);
CAPIHook CAPIHook::sm_GetProcAddress("Kernel32.dll", "GetProcAddress",(PROC) CAPIHook::GetProcAddress, true);
//新的LoadLibrary的函数
void CAPIHook::FixupNewlyLoadedModule(HMODULE hmod, DWORD dwFlags)
{
 // If a new module is loaded, hook the hooked functions
 if ((hmod != NULL) && ((dwFlags & LOAD_LIBRARY_AS_DATAFILE) == 0))
 {
  for (CAPIHook* p = sm_pHead; p != NULL; p = p->m_pNext)
  {
   ReplaceIATEntryInOneMod(p->m_pszCalleeModName,p->m_pfnOrig, p->m_pfnHook, hmod);
  }
 }
}
//新的LoadLibraryA
HMODULE WINAPI CAPIHook::LoadLibraryA(PCSTR pszModulePath)
{
 HMODULE hmod = ::LoadLibraryA(pszModulePath);
 FixupNewlyLoadedModule(hmod, 0);
 return(hmod);
}
//新的LoadLibraryW
HMODULE WINAPI CAPIHook::LoadLibraryW(PCWSTR pszModulePath)
{
 HMODULE hmod = ::LoadLibraryW(pszModulePath);
 FixupNewlyLoadedModule(hmod, 0);
 return(hmod);
}
//新的LoadLibraryExA
HMODULE WINAPI CAPIHook::LoadLibraryExA(PCSTR pszModulePath,HANDLE hFile, DWORD dwFlags)
{
 HMODULE hmod = ::LoadLibraryExA(pszModulePath, hFile, dwFlags);
 FixupNewlyLoadedModule(hmod, dwFlags);
 return(hmod);
}
//新的LoadLibraryExW
HMODULE WINAPI CAPIHook::LoadLibraryExW(PCWSTR pszModulePath,HANDLE hFile, DWORD dwFlags)
{
 HMODULE hmod = ::LoadLibraryExW(pszModulePath, hFile, dwFlags);
 FixupNewlyLoadedModule(hmod, dwFlags);
 return(hmod);
}
//新的GetProcAddress
FARPROC WINAPI CAPIHook::GetProcAddress(HMODULE hmod, PCSTR pszProcName)
{
 // Get the true address of the function
 FARPROC pfn = GetProcAddressRaw(hmod, pszProcName);
 // Is it one of the functions that we want hooked?
 CAPIHook* p = sm_pHead;
 for (; (pfn != NULL) && (p != NULL); p = p->m_pNext)
 {
  if (pfn == p->m_pfnOrig)
  {
   // The address to return matches an address we want to hook
   // Return the hook function address instead
   pfn = p->m_pfnHook;
   break;
  }
 }
 return pfn;
}
#pragma managed
 
 
//MyASSERT.h
#pragma once
#pragma comment(lib, "User32.lib")
#define chDIMOF(Array) (sizeof(Array) / sizeof(Array[0]))
inline void chMB(PCSTR s)
{
 char szTMP[128];
 ::GetModuleFileNameA(NULL, szTMP, chDIMOF(szTMP));
 MessageBoxA(GetActiveWindow(), s, szTMP, MB_OK);
}
inline void chFAIL(PSTR szMsg)
{
 chMB(szMsg);
 DebugBreak();
}
// Put up an assertion failure message box.
inline void chASSERTFAIL(LPCSTR file, int line, PCSTR expr)
{
 char sz[128];
 wsprintfA(sz, "File %s, line %d : %s", file, line, expr);
 chFAIL(sz);
}
#ifdef _DEBUG
#define chASSERT(x) if (!(x)) chASSERTFAIL(__FILE__, __LINE__, #x)
#else
#define chASSERT(x)
#endif