简明的Detours Hook教程

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函数,他们可以在目标进程里分配内存,可以将你的进程中的数据拷贝到目标进程中。

 抛砖引玉。欢迎臭鸡蛋!

参与评论 您还未登录,请先 登录 后发表或查看评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页

打赏作者

allenq

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值