简明的Detours Hook教程

来源:互联网 发布:淘宝双十一大红包 编辑:程序博客网 时间:2024/06/05 02:48

tag: Hook, Detours,Windows,CreateRemoteThread,MessageBox

前言
    项目开发中需要跟踪其它程序的API调用情况。但厂商又无源码提供,故只好自己动手去Trace了。
    Google/Baidu了许久,也搜集了很多代码。也经过实验和测试,总结了本文供大家参考。
    本文针对Windows Hook技术在编程中的应用进行讨论,并着重对应用比较广泛的Detours使用方法做了阐述。

Hook/钩子的基本原理

   Windows 钩子的本质是一段用以处理系统消息的程序,通过系统调用,将其挂入到系统。钩子的种类有很多,每一种钩子负责截获并处理相应的消息。钩子机制允许应用程序截获并处理发往指定窗口的消息或特定事件,其监视的窗口即可以是本进程内的也可以是由其他进程所创建的。在特定的消息发出,并在到达目的窗口之前,钩子程序先行截获此消息并得到对其的控制权。此时在钩子函数中就可以对截获的消息进行各种修改处理,甚至强行终止该消息的继续传递。
 Detours库是Microsoft研究院的一个开发库,最新版本为3.0。它可以拦截任意的API调用,拦截代码是在动态运行时加载的,替换目标API最前面的几条指令,使其无条件的跳转到用户提供的拦截函数。
 
Hook/钩子的安装与卸载

   开始代码了,真的非常easy!以Hook MessageBox为例:

  准备工作:Detours lib/h files, VC++ 6/2005/2008....

  A. 封装Dll.

  1. VC++向导创建一个MFC Dll项目,一路向西,默认缺省选项。

   改写DllMain如下: 

BOOL APIENTRY DllMain( HMODULE hModule,                       DWORD  ul_reason_for_call,                       LPVOID lpReserved ){    switch (ul_reason_for_call){case DLL_PROCESS_ATTACH:::OutputDebugString( "DLL_PROCESS_ATTACH\n" );InstallHook();break;case DLL_THREAD_ATTACH:break;case DLL_THREAD_DETACH:break;case DLL_PROCESS_DETACH:::OutputDebugString( "DLL_PROCESS_DETACH\n" );UnInstallHook();break;}return TRUE;}

  2. 增加InstallHook/UnInstallHook函数:

////////////BOOL APIENTRY InstallHook(){DetourTransactionBegin();DetourUpdateThread( GetCurrentThread() );g_pOldMessageBoxA = DetourFindFunction( "User32.dll","MessageBoxA" );g_pOldMessageBoxW = DetourFindFunction( "User32.dll","MessageBoxW" );DetourAttach( &g_pOldMessageBoxA, MyMessageBoxA );DetourAttach( &g_pOldMessageBoxW, MyMessageBoxW );LONG ret = DetourTransactionCommit();return ret==NO_ERROR;}////////////BOOL APIENTRY UnInstallHook(){DetourTransactionBegin();DetourUpdateThread( GetCurrentThread() );DetourDetach(&g_pOldMessageBoxA, MyMessageBoxA);DetourDetach(&g_pOldMessageBoxW, MyMessageBoxW);LONG ret=DetourTransactionCommit();return ret==NO_ERROR;}

  注意上文代码中的MessageBoxA和MessageBoxW字样,这就是我们所要拦截的API函数咯。至于为何不是MessageBox,却是MessageBoxA和MessageBoxW这样的怪样子,新手们去看看Windows核心编程啦。
 3. 我们的拦截函数

typedef int (WINAPI *PfuncMessageBoxA)(HWND hWnd,LPCSTR lpText,LPCSTR lpCaption,UINT uType);typedef int (WINAPI *PfuncMessageBoxW)( HWND hWnd, LPCWSTR lpText,LPCWSTR lpCaption,UINT uType);////////////int WINAPI MyMessageBoxA(HWND hWnd,LPCSTR lpText,LPCSTR lpCaption,UINT uType){return ((PfuncMessageBoxA)g_pOldMessageBoxA)(hWnd, "Hook This!","My hook",uType);}////////////int WINAPI MyMessageBoxW(HWND hWnd, LPCWSTR lpText,LPCWSTR lpCaption,UINT uType){return ((PfuncMessageBoxW)g_pOldMessageBoxW)(hWnd,L"Hook This!",L"My hook",uType);}


 此例中我们只是更改了MessageBox的Text和Caption。

4.  记得保留原版的MessageBox。

   build一下,发现错误:g_pOldMessageBoxA还未定义? 还记得上文中的 

g_pOldMessageBoxA = DetourFindFunction( "User32.dll","MessageBoxA" );

 这个函数指针需要保留。

PVOID g_pOldMessageBoxW=NULL;
PVOID g_pOldMessageBoxA=NULL;

 DLL就万事大吉,只欠东风了。

B. 注射器

 所谓的注射器,就是东风了,要不然光有dll也用,还需要用注射器把我们的dll注入到肉鸡中。老生常谈,还是老一套的CreateRemoteThread。这个方法是如此的简单而且优雅。

HANDLE hProcess = OpenProcess( PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ,FALSE, pid);if (hProcess != NULL){TRACE( "InjectHook \n" );HANDLE hThread;char   szLibPath [_MAX_PATH];void*  pLibRemote = 0;DWORD  hLibModule = 0;HMODULE hKernel32 = ::GetModuleHandle("Kernel32");if( !::GetSystemDirectory(szLibPath, _MAX_PATH))return;strcat(szLibPath, "C:\\windows\\HookDll.dll");pLibRemote = ::VirtualAllocEx( hProcess, NULL, sizeof(szLibPath), MEM_COMMIT, PAGE_READWRITE );if( pLibRemote == NULL )return;::WriteProcessMemory(hProcess, pLibRemote, (void*)szLibPath,sizeof(szLibPath),NULL);hThread = ::CreateRemoteThread( hProcess, NULL, 0,(LPTHREAD_START_ROUTINE) ::GetProcAddress(hKernel32,"LoadLibraryA"), pLibRemote, 0, NULL );if( hThread != NULL ){::WaitForSingleObject( hThread, INFINITE );::GetExitCodeThread( hThread, &hLibModule );::CloseHandle( hThread );}}

解释一下以上的szLibPath, VirtualAllocEx, WriteProcessMemory, CreateRemoteThread。

这是远线程,不是在你的进程里,而szLibPath指向的是你的进程里的数据,到了目标进程,这个指针都不知道指向哪儿去了,同样CreateRemoteThread中的pfnStartAddr这个地址上的代码到了目标进程里也不知道是什么了,不知道是不是你想要的LoadLibraryA了。但是,问题总是可以解决的,Windows有些很强大的API函数,他们可以在目标进程里分配内存,可以将你的进程中的数据拷贝到目标进程中。

 抛砖引玉。欢迎臭鸡蛋!

原创粉丝点击