dotnet下的Hook API
版权声明:原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://happyoldbear.blog.51cto.com/231048/48634 |
/// <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 本文出自 “欢乐老熊” 博客,请务必保留此出处http://happyoldbear.blog.51cto.com/231048/48634 本文出自 51CTO.COM技术博客 |
附件下载:
源代码:
源代码:


lijun4183
博客统计信息
热门文章
最新评论
友情链接
