Microsoft Detours 2.1简介

NetRoc

http://www.DbgTech.net/

本文从主站点转贴过来的,附件和pdf请访问http://www.DbgTech.net/下载

一、简介

《Windows高级调试》第一章中提到了一个基于Microsoft Detours库的内存泄露检查工具LeakDiag。本文对这个库进行一些介绍。

一句话来说,Detours是一个用来在二进制级别上对程序中的函数(Function)或者过程(Procedure)进行修改的工具库。一般我们将这种技术称为"Hook"。Detours的实现原理是将目标函数的前几个字节改为jmp指令跳转到自己的函数地址,以此接管对目标函数的调用,并插入自己的处理代码。在现实中,这种技术可以应用在很多场景下。比如Hook某些Windows API,在实际调用到系统函数前进行一些过滤工作;软件中使用到了一些没有源代码的第三方库,但是又想增强其中某些函数的功能,等等。

图1 Hook前后的程序执行流程对比。

 

图2 Hook前后目标函数和跳板代码的改变

 

Detours相对其他一些Hook库和自己实现的代码来说,通常有以下这些优点:

  • 考虑全面,代码非常稳定,并且经过了微软自己众多产品的验证。
  • 可以简单的用纯C/C++代码实现对类的成员函数的Hook。
  • 购买版权之后的Detours Professional还可以支持x64和IA64处理器。以此为基础编写的代码拥有更强的可移植性。
  • 使用简单,不需要了解汇编指令以及技术细节。

二、使用方法

一般来说,使用Detours的代码都具有固定的模式。Detours 1.5和Detours 2.1的接口函数变了很多,这里按照2.1版本对基本的使用方法进行说明。

常用的函数有下面几个:

  • DetourTransactionBegin() :开始一次Hook或者Unhook过程。
  • DetourUpdateThread() :列入一个在DetourTransaction过程中要进行update的线程。这个函数的作用稍微有一些复杂,会在后面专门说明。
  • DetourAttach() :添加一个要Hook的函数。
  • DetourDetach () :添加一个要Unhook的函数。
  • DetourTransactionCommit() :执行当前的Transaction过程。在这个函数中才会真正进行Hook或者Unhook操作。前面三个函数都只是做一些记录工作。

在使用的时候,这几个函数的调用步骤基本上也是按照上面列出来的顺序。举例来说,现在想Hook掉API函数MessageBoxA,将消息框弹出的消息修改掉,可以按下面的方法做。

进行Hook的步骤:

  1. 首先需要定义目标函数的原型。如果目标函数是Windows API,可以到MSDN中查阅,但是需要注意ANSI版本和Unicode版本的区别。如果没有确切的原型声明,或者目标函数是通过逆向工程找出来的,那么需要定义一个和目标函数原型兼容的声明,即参数个数和调用约定要相同。如MessageBoxA的原型是:

    int MessageBoxA( HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);

    使用typedef定义如下:

    typedef int (WINAPI *pfnMessageBoxA)( HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);

  2. 声明一个指向目标函数的函数指针:

    pfnMessageBoxA g_pMessageBoxA = ::MessageBoxA;

  3. 编写Hook函数的代码,用于替换目标函数。
  4. 调用 DetourTransactionBegin开始一次Detours事务。
  5. 对进程中每个可能调用到目标函数的线程,都需要使用 DetourUpdateThread加入到update队列中。这是因为Hook时修改目标函数的前几个字节,如果某个线程刚好执行到这几个字节的位置时,粗暴的修改掉会造成该线程出现异常。Detours事务处理时,会先枚举并暂停update队列中所有线程,获取它们的指令指针,如果发现这种情况,则将指令指针修改到跳板代码的对应字节上。这样就避免出现崩溃的问题。
  6. 对每个需要Hook的函数,调用 DetourAttach加入到事务列表中。
  7. 调用 DetourTransactionCommit进行实际的Hook操作。

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库的代码是非常稳定的,但是如果使用方法不对,会造成一些问题。有下面一些地方需要特别注意:

  1. 一定要枚举线程并调用DetourUpdateThread函数。否则可能出现很低几率的崩溃问题,这种问题很难被检查出来。
  2. 如果Hook函数在DLL中,那么绝大多数情况下不能在Unhook之后卸载这个DLL,或者卸载存在造成崩溃的危险。因为某些线程的调用堆栈中可能还包含Hook函数,这时卸载掉DLL,调用堆栈返回到Hook函数时内存位置已经不是合法的代码了。
  3. Detours库设计时并没有考虑到卸载的问题,这是因为钩子的卸载本身是不安全的。当Detours库代码存在于DLL中的时候,即使Unhook了所有函数,清理了所有自己使用到的函数,还是会占用一些内存。卸载这个DLL会造成内存泄露,特别是反复的进行加载DLL->Hook->Unhook->卸载DLL的过程,会让这个问题变得非常严重。后面会用一篇专题文章来讨论Detours内存泄露问题的调试和解决。
  4. 有一些非常短的目标函数是无法Hook的。因为jmp指令需要占用一定空间,有些函数太过短小,甚至不够jmp指令的长度,自然是没有办法Hook掉的。

Detours不支持9x内核的Windows系统。因为9x内核下的内存模型和NT内核下有非常大的差别。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Detours是微软开发的一个函数库, 用于修改运行中的程序在内存中的影像,从而即使没有源代码也能改变程序的行为。具体用途是: 拦截WIN32 API调用,将其引导到自己的子程序,从而实现WIN32 API的定制。 为一个已在运行的进程创建一新线程,装入自己的代码并运行。 ---- 本文将简介Detours的原理,Detours库函数的用法, 并利用Detours库函数在Windows NT上编写了一个程序,该程序能使有“调试程序”的用户权限的用户成为系统管理员,附录利用Detours库函数修改该程序使普通用户即可成为系统管理员 (在NT4 SP3上)。 一. Detours的原理 ---- 1. WIN32进程的内存管理 ---- 总所周知,WINDOWS NT实现了虚拟存储器,每一WIN32进程拥有4GB的虚存空间, 关于WIN32进程的虚存结构及其操作的具体细节请参阅WIN32 API手册, 以下仅指出与Detours相关的几点: ---- (1) 进程要执行的指令也放在虚存空间中 ---- (2) 可以使用QueryProtectEx函数把存放指令的页面的权限更改为可读可写可执行,再改写其内容,从而修改正在运行的程序 ---- (3) 可以使用VirtualAllocEx从一个进程为另一正运行的进程分配虚存,再使用 QueryProtectEx函数把页面的权限更改为可读可写可执行,并把要执行的指令以二进制机器码的形式写入,从而为一个正在运行的进程注入任意的代码 ---- 2. 拦截WIN32 API的原理 ---- Detours定义了三个概念: ---- (1) Target函数:要拦截的函数,通常为WindowsAPI。 ---- (2) Trampoline函数:Target函数的复制品。因为Detours将会改写Target函数,所以先把Target函数复制保存好,一方面仍然保存Target函数的过程调用语义,另一方面便于以后的恢复。 ---- (3) Detour 函数:用来替代Target函数的函数。 ---- Detours在Target函数的开头加入JMP Address_of_ Detour_ Function指令(共5个字节)把对Target函数的调用引导到自己的Detour函数, 把Target函数的开头的5个字节加上JMP Address_of_ Target _ Function+5作为Trampoline函数。例子如下: 拦截前:Target _ Function: ;Target函数入口,以下为假想的常见的子程序入口代码 push ebp mov ebp, esp push eax push ebx Trampoline: ;以下是Target函数的继续部分 …… 拦截后: Target _ Function: jmp Detour_Function Trampoline: ;以下是Target函数的继续部分 …… Trampoline_Function: ; Trampoline函数入口, 开头的5个字节与Target函数相同 push ebp mov ebp, esp push eax push ebx ;跳回去继续执行Target函数 jmp Target_Function+5 ---- 3. 为一个已在运行的进程装入一个DLL ---- 以下是其步骤: ---- (1) 创建一个ThreadFuction,内容仅是调用LoadLibrary。 ---- (2) 用VirtualAllocEx为一个已在运行的进程分配一片虚存,并把权限更改为可读可写可执行。 ---- (3) 把ThreadFuction的二进制机器码写入这片虚存。 ---- (4) 用CreateRemoteThread在该进程上创建一个线程,传入前面分配的虚存的起始地址作为线程函数的地址,即可为一个已在运行的进程装入一个DLL。通过DllMain 即可在一个已在运行的进程中运行自己的代码。 二. Detours库函数的用法 ---- 因为Detours软件包并没有附带帮助文件,以下接口仅从剖析源代码得出。 ---- 1. PBYTE WINAPI DetourFindFunction(PCHAR pszModule, PCHAR pszFunction) ---- 功能:从一DLL中找出一函数的入口地址 ---- 参数:pszModule是DLL名,pszFunction是函数名。 ---- 返回:名为pszModule的DLL的名为pszFunction的函数的入口地址 ---- 说明:DetourFindFunctio

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值