NetRoc
本文从主站点转贴过来的,附件和pdf请访问http://www.DbgTech.net/下载
《Windows高级调试》第一章中提到了一个基于Microsoft Detours库的内存泄露检查工具LeakDiag。本文对这个库进行一些介绍。
一句话来说,Detours是一个用来在二进制级别上对程序中的函数(Function)或者过程(Procedure)进行修改的工具库。一般我们将这种技术称为"Hook"。Detours的实现原理是将目标函数的前几个字节改为jmp指令跳转到自己的函数地址,以此接管对目标函数的调用,并插入自己的处理代码。在现实中,这种技术可以应用在很多场景下。比如Hook某些Windows API,在实际调用到系统函数前进行一些过滤工作;软件中使用到了一些没有源代码的第三方库,但是又想增强其中某些函数的功能,等等。
图1 Hook前后的程序执行流程对比。
图2 Hook前后目标函数和跳板代码的改变
Detours相对其他一些Hook库和自己实现的代码来说,通常有以下这些优点:
一般来说,使用Detours的代码都具有固定的模式。Detours 1.5和Detours 2.1的接口函数变了很多,这里按照2.1版本对基本的使用方法进行说明。
常用的函数有下面几个:
在使用的时候,这几个函数的调用步骤基本上也是按照上面列出来的顺序。举例来说,现在想Hook掉API函数MessageBoxA,将消息框弹出的消息修改掉,可以按下面的方法做。
进行Hook的步骤:
int MessageBoxA( HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);
使用typedef定义如下:
typedef int (WINAPI *pfnMessageBoxA)( HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);
pfnMessageBoxA g_pMessageBoxA = ::MessageBoxA;
Unhook的过程和上面的流程基本一样,只是第6步改为调用DetourDetach函数。
Hook MessageBoxA的完整示例代码如下:
//Hook函数的向前声明
int WINAPI Hook_MessageBoxA( HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);
//目标函数原型声明
typedef int (WINAPI *pfnMessageBoxA)( HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);
//指向目标函数的指针
pfnMessageBoxA g_pMessageBoxA = ::MessageBoxA;
BOOL StartHook()
{//开始Hook
DetourTransactionBegin();
//只有一个线程,所以GetCurrentThread
DetourUpdateThread( GetCurrentThread());
//添加MessageBoxA的Hook
if( DetourAttach( &(PVOID&)g_pMessageBoxA, Hook_MessageBoxA) != NO_ERROR)
{
printf( "Hook MessageBoxA fail.\n");
}
//完成事务
if( DetourTransactionCommit() != NO_ERROR)
{
printf( "DetourTransactionCommit fail\n");
return FALSE;
}
else
{
printf( "DetourTransactionCommit ok\n");
return TRUE;
}
}
BOOL StopHook()
{//停止Hook
DetourTransactionBegin();
DetourUpdateThread( GetCurrentThread());
if( DetourDetach( &(PVOID&)g_pMessageBoxA, Hook_MessageBoxA) != NO_ERROR)
{
printf( "Hook MessageBoxA fail.\n");
}
if( DetourTransactionCommit() != NO_ERROR)
{
printf( "DetourTransactionCommit fail\n");
return FALSE;
}
else
{
printf( "DetourTransactionCommit ok\n");
return TRUE;
}
}
int WINAPI Hook_MessageBoxA( HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
{
//需要调用原函数时,可以直接使用前面定义的指针变量
return g_pMessageBoxA( hWnd, "MessageBox after hook.", "TestDetours", MB_OK);
}
在附件的示例代码中还包含了Hook类成员函数的代码。流程和上面基本一致,只是需要用一些强制转换来对付编译器的类型检查。
另外,Detours还包含一系列其他函数,如果需要使用的话,可以参考Detours安装目录下的示例。
总体来说,Detours库的代码是非常稳定的,但是如果使用方法不对,会造成一些问题。有下面一些地方需要特别注意:
Detours不支持9x内核的Windows系统。因为9x内核下的内存模型和NT内核下有非常大的差别。