Detours库Windows API Hook

什么是Detours 
简单地说,Detours是微软提供的一个开发库,使用它可以简单、高校、稳定地实现API HOOK的功能。

  Detours是一个可以在x86、x64和IA64平台上测试任意Win32函数的程序开发库。它可以通过为目标函数重写在内存中的代码而达到拦截Win32函数的目的。Detours还可以将任意的DLL或数据片段(称之为有效载荷)注入到任意Win32二进制文件中。Detours 被广泛应用在微软内部和其他行业中。 

 

在使用 Detours 劫持之前必须得拥有这两个东西:detours.h 和 detours.lib

从GitHub上下载源码

https://github.com/microsoft/Detours

打开VS的开发人员命令提示符

cd 进入下载的目录         G:\C++工具源码\Detours-master

键入 nmake          开始编译

编译完成后

bin.X86  里面是编译出来的Demo

lib.X86   detours静态库

include   头文件

将这2个文件添加到自己的项目中

LONG DetourAttach(
     PVOID * ppPointer,
     PVOID pDetour     
);

这个函数的职责是挂接目标API,函数的第一个参数是一个指向将要被挂接函数地址的函数指针,第二个参数是指向实际运行的函数的指针,一般来说是我们定义的替代函数的地址。但是,在挂接开始之前,还有以下几件事需要完成: 需要对detours进行初始化.  需要更新进行detours的线程.  这些可以调用以下函数很容的做到: 

DetourRestoreAfterWith()   //恢复之前状态,避免反复拦截
DetourTransactionBegin()  //开始劫持
DetourUpdateThread(GetCurrentThread())//刷新当前的线程
DetourTransactionCommit()        //提交修改,立即HOOk

在这两件事做完以后,detour函数才是真正地附着到目标函数上。在此之后,调用DetourTransactionCommit()是detour函数起作用并检查函数的返回值判断是正确还是错误。 

#include "stdafx.h"
#include<detours.h>
#include<Windows.h>
#pragma comment(lib,"detours.lib")

void HookOn();
void HookOff();

static int (WINAPI *OldMesssageBoxA)
(
    HWND hWnd,
    LPCSTR lpText,
    LPCSTR lpCaption,
    UINT uType
) = MessageBoxA;

static int (WINAPI *OldMesssageBoxW)
(
    HWND hWnd,
    LPCWSTR lpText,
    LPCWSTR lpCaption,
    UINT uType
) = MessageBoxW;

// 注意了,自定义函数得和被 HOOK 的函数一样,否则会发生异常。
int WINAPI MyFunction0
(
    HWND hWnd,
    LPCSTR lpText,
    LPCSTR lpCaption,
    UINT uType
);

int WINAPI MyFunction1
(
    HWND hWnd,
    LPCWSTR lpText,
    LPCWSTR lpCaption,
    UINT uType
);

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        HookOn();
        break;
    case DLL_THREAD_ATTACH:
        break;
    case DLL_THREAD_DETACH:
        break;
    case DLL_PROCESS_DETACH:
        HookOff();
        break;
    }
    return TRUE;
}

void HookOn()
{
    //开始事务
    DetourTransactionBegin();
    //更新线程信息  
    DetourUpdateThread(GetCurrentThread());
    //将拦截的函数附加到原函数的地址上,这里可以拦截多个函数。
    DetourAttach(&(PVOID&)OldMesssageBoxA, MyFunction0);
    DetourAttach(&(PVOID&)OldMesssageBoxW, MyFunction1);
    //结束事务
    DetourTransactionCommit();
}

void HookOff()
{
    //开始事务
    DetourTransactionBegin();
    //更新线程信息 
    DetourUpdateThread(GetCurrentThread());
    //将拦截的函数从原函数的地址上解除,这里可以解除多个函数。
    DetourDetach(&(PVOID&)OldMesssageBoxA, MyFunction0);
    DetourDetach(&(PVOID&)OldMesssageBoxW, MyFunction1);
    //结束事务
    DetourTransactionCommit();
}

// 调用被 HOOK 的函数可以用被 HOOK 函数的指针,不能用原函数。
int WINAPI MyFunction0(HWND hWnd,LPCSTR lpText,LPCSTR lpCaption,UINT uType)
{
     return OldMesssageBoxA(NULL, "Hooking your MessageBoxA!", "Warming", MB_OKCANCEL);
}

int WINAPI MyFunction1(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption,UINT uType)
{
    return OldMesssageBoxW(NULL, L"Hooking your MessageBoxW!", L"Warming", MB_OKCANCEL);
}

原理

Detours定义了三个概念:

(1) Target函数:要拦截的函数,通常为Windows的API。
(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

HOOK后的MessageBoxA

替换后的MyFunction0

 调用MessageBox

 

上面是5字节的HOOK,如果函数的开始5字节不是完整的汇编指令集,Detours会自动Hook大于5字节且在保证最少字节数的情况下汇编指令完整的。

HOOK之前

HOOK之后   HOOK了8字节

 

利用Detours实现任意位置的HOOK

PVOID g_pTestHook;

void TestSrc()
{
	MessageBoxA(NULL, "TestSrc", "", 0);
}

void  __declspec(naked) TestHooK()
{
	MessageBoxA(NULL, "Test Src Start", "", 0);
	MessageBoxA(NULL, "Test Src End", "", 0);

	_asm {
		jmp g_pTestHook
	}
}

void HookOn()
{
	TestSrc();

	g_pTestHook = PVOID((DWORD)TestSrc + 1);  //HOOK 函数的第二个字节

	DetourTransactionBegin();

	DetourUpdateThread(GetCurrentThread());

	DetourAttach(&g_pTestHook, TestHooK);

	DetourTransactionCommit();

	TestSrc();
}

库函数介绍

HOOK相关的函数

DetourTransactionBegin

LONG DetourTransactionBegin(VOID);
//如果成功,则返回NO_ERROR;否则,返回0。否则,返回错误代码。

开始事务

开始事务之后,程序可以调用 DetourAttach或 DetourAttachEx API HOOK目标函数,调用DetourDetach API恢复目标函数,或者调用 DetourUpdateThread API在事务更新中线程。

在程序调用DetourTransactionCommit或 DetourTransactionCommitEx API 提交事务之后,附加,分离和线程操作才会生效 。或者,程序可以使用DetourTransactionAbort API 中止事务 。

 

DetourUpdateThread

LONG DetourUpdateThread(
    _In_ HANDLE hThread        //待处理事务更新线程的句柄
    );

//如果成功,则返回NO_ERROR;否则,返回0。否则,返回错误代码。

DetourUpdateThreadDetourTransactionBegin API 打开的事务提交时,使用指定的线程进行更新 。

提交事务时,不会更新未在事务中登记的线程。结果,他们可能试图执行新旧代码的非法组合。

 

DetourAttach

LONG DetourAttach(
    _Inout_ PVOID * ppPointer, //被HOOk函数地址   这个一个二级指针成功调用之后被改变成新的函数地址 前5个字节然后jmp到原函数的第六个字节
    _In_    PVOID pDetour        //替换的函数地址
    );

HOOK函数

 

DetourAttachEx

LONG DetourAttachEx(
    _Inout_   PVOID * ppPointer, //被HOOk函数地址   这个一个二级指针成功调用之后被改变成新的函数地址 前5个字节然后jmp到原函数的第六个字节
    _In_      PVOID pDetour,  //替换的函数地址
    _Out_opt_ PDETOUR_TRAMPOLINE * ppRealTrampoline //用于接收中转函数地址的信息 包括中转函数的地址,代码(前5个字节)等
    _Out_opt_ PVOID * ppRealTarget  //用于接收目标函数的最终地址
    _Out_opt_ PVOID * ppRealDetour  //用于接收替换函数的最终地址
    );


struct _DETOUR_TRAMPOLINE
{
    BYTE            rbCode[30];     // target code + jmp to pbRemain 中转函数地址
    BYTE            cbCode;         // size of moved target code.  修改前几个字节  5
    BYTE            cbCodeBreak;    // padding to make debugging easier.
    BYTE            rbRestore[22];  // original target code.
    BYTE            cbRestore;      // size of original target code.
    BYTE            cbRestoreBreak; // padding to make debugging easier.
    _DETOUR_ALIGN   rAlign[8];      // instruction alignment array.
    PBYTE           pbRemain;       // first instruction after moved code. [free list]
    PBYTE           pbDetour;       // first instruction of detour function.
};

HOOK函数

 

 

DetourDetach

LONG DetourDetach(
    _Inout_ PVOID * ppPointer,  //被HOOK函数地址的二级指针
    _In_    PVOID pDetour        //替换函数的地址
    );

取消HOOK

 

用于查找目标函数的API

DetourFindFunction

PVOID DetourFindFunction(
    _In_ LPCSTR pszModule,  //应在其中找到函数的DLL或二进制文件的路径。
    _In_ LPCSTR pszFunction  //要找到的函数的名称。
    );
//如果成功,则返回函数pszFunction的地址;否则,返回NULL。

DetourFindFunction 尝试通过动态链接命名模块的导出表来检索命名函数的函数指针,如果失败,则使用DbgHelp API(如果可用)调试符号。

 

DetourCodeFromPointer

PVOID DetourCodeFromPointer(
    _In_      PVOID pPointer,  //指向函数的指针。
    _Out_opt_ PVOID *ppGlobals //变量,用于接收函数的全局数据的地址。
    );
//返回指向实现该功能的代码的指针。

返回一个指向实现函数指针的代码的指针。

DetourCodeFromPointer返回指向实现函数指针所指向的函数的代码的指针。当二进制文件与DLL静态链接时,指向DLL函数的指针通常不指向DLL中的代码,而是指向二进制文件的导入表中的跳转语句。DetourCodeFromPointer返回实际目标函数的地址,而不是跳转语句。

 

用于获取加载的二进制文件信息的API

DetourEnumerateModules

HMODULE DetourEnumerateModules(
    _In_opt_ HMODULE hModuleLast  //终止模块的句柄,NULL枚举全部
    );

枚举模块

 

DetourGetEntryPoint

PVOID DetourGetEntryPoint(
    _In_opt_ HMODULE hModule   //模块句柄
    );

返回模块的入口点。

 

DetourGetModuleSize

ULONG DetourGetModuleSize(
    _In_ HMODULE hModule   //模块句柄
    );

返回模块的加载大小。

 

DetourEnumerateExports

BOOL DetourEnumerateExports(
    _In_     HMODULE hModule,  //模块的句柄
    _In_opt_ PVOID pContext,   //程序的上下文
    _In_     PF_DETOUR_ENUMERATE_EXPORT_CALLBACK pfExport  //枚举回调函数
    );

typedef BOOL (CALLBACK *PF_DETOUR_ENUMERATE_EXPORT_CALLBACK)(_In_opt_ PVOID pContext,
                                                             _In_ ULONG nOrdinal,
                                                             _In_opt_ LPCSTR pszName,
                                                             _In_opt_ PVOID pCode);

枚举模块的导出函数

 

DetourEnumerateImports

BOOL DetourEnumerateImports(
    _In_opt_ HMODULE hModule,  //模块的句柄
    _In_opt_ PVOID pContext,  //特定于程序的上下文,该上下文将传递给pfImportFile和 pfImportFunc
    _In_opt_ PF_DETOUR_IMPORT_FILE_CALLBACK pfImportFile, //每个模块导入的文件将调用一次回调函数
    _In_opt_ PF_DETOUR_IMPORT_FUNC_CALLBACK pfImportFunc  //每个模块导入的函数将调用一次回调函数
    );

typedef BOOL (CALLBACK *PF_DETOUR_IMPORT_FILE_CALLBACK)(_In_opt_ PVOID pContext,
                                                        _In_opt_ HMODULE hModule,
                                                        _In_opt_ LPCSTR pszFile);

typedef BOOL (CALLBACK *PF_DETOUR_IMPORT_FUNC_CALLBACK)(_In_opt_ PVOID pContext,
                                                        _In_ DWORD nOrdinal,
                                                        _In_opt_ LPCSTR pszFunc,
                                                        _In_opt_ PVOID pvFunc);

枚举导入函数

 

DetourEnumerateImportsEx

BOOL DetourEnumerateImportsEx(
    _In_opt_ HMODULE hModule,   //模块的句柄
    _In_opt_ PVOID pContext,  //上下文将传递给pfImportFile和 pfImportFunc
    _In_opt_ PF_DETOUR_IMPORT_FILE_CALLBACK pfImportFile,  //每个模块导入的文件将调用一次回调函数
    _In_opt_ PF_DETOUR_IMPORT_FUNC_CALLBACK_EX pfImportFunc  //每个模块导入的函数将调用一次回调函数
    );


typedef BOOL (CALLBACK *PF_DETOUR_IMPORT_FUNC_CALLBACK_EX)(_In_opt_ PVOID pContext,
                                                           _In_ DWORD nOrdinal,
                                                           _In_opt_ LPCSTR pszFunc,
                                                           _In_opt_ PVOID* ppvFunc);

枚举从模块导入函数

 

DetourFindPayload

_Writable_bytes_(*pcbData)
_Readable_bytes_(*pcbData)
_Success_(return != NULL)
PVOID DetourFindPayload(
    _In_opt_ HMODULE hModule,  //模块句柄
    _In_     REFGUID rguid,
    _Out_    DWORD * pcbData
    );

定位二进制文件中映射的payloads

 

DetourGetContainingModule

HMODULE DetourGetContainingModule(
    _In_ PVOID vpAddr         //函数地址
    );

根据函数地址,查找所在的模块

 

DetourGetSizeOfPayloads

DWORD DetourGetSizeOfPayloads(
    _In_ HMODULE hModule
    );

获取Payload的大小

 

修改PE文件相关的API

  1. DetourBinaryOpen打开一个PE二进制文件。
  2. DetourBinaryEnumeratePayloads枚举二进制文件中的payloads。
  3. DetourBinaryFindPayload查找指定的payload。
  4. DetourBinarySetPayload设置或者替换一个指定的payload。
  5. DetourBinaryDeletePayload删除一个指定的payload。
  6. DetourBinaryPurgePayloads删除二进制中的所有payloads。
  7. DetourBinaryEditImportsModifythePEbinaryimporttable。
  8. DetourBinaryResetImports复位PE二进制导入地址表。
  9. DetourBinaryWrite把PE映像写入到一个文件中。
  10. DetourBinaryClose关闭PE二进制映像。
  11. DetourBinaryBind使用BindImage绑定二进制映像。

 

DLL和区段注入进程相关的API

DetourCreateProcessWithDllEx

BOOL DetourCreateProcessWithDllEx(
    _In_opt_    LPCTSTR lpApplicationName,  //CreateProcess API定义的应用程序名称
    _Inout_opt_ LPTSTR lpCommandLine,   //CreateProcess API定义的命令行
    _In_opt_    LPSECURITY_ATTRIBUTES lpProcessAttributes, //CreateProcess API定义的流程属性
    _In_opt_    LPSECURITY_ATTRIBUTES lpThreadAttributes, //CreateProcess API定义的线程属性
    _In_        BOOL bInheritHandles,  //CreateProcess API定义的句柄标志
    _In_        DWORD dwCreationFlags,  //CreateProcess API定义的创建标志
    _In_opt_    LPVOID lpEnvironment,    //CreateProcess API定义的流程环境变量
    _In_opt_    LPCTSTR lpCurrentDirectory, //CreateProcess API定义的当前目录
    _In_        LPSTARTUPINFOW lpStartupInfo,  //CreateProcess API定义的启动信息
    _Out_       LPPROCESS_INFORMATION lpProcessInformation,  //CreateProcess API定义的处理句柄信息
    _In_        LPCSTR lpDllName, //要插入到新进程中的DLL的路径名。为了同时支持32位和64位应用程序,如果DLL包含32位代码,则DLL名称应以“ 32”结尾;如果DLL包含64位代码,则DLL名称应以“ 64”结尾。如果目标进程的大小与父进程的大小不同,则Detours会自动在路径名中将“ 32”替换为“ 64”或将“ 64”替换为“ 32”。
    _In_opt_    PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW //指向程序特定替代CreateProcess API的指针;如果应使用标准CreateProcess API创建新进程,则为NULL。
    );

启动程序并注入DLL

 

DetourCreateProcessWithDlls

BOOL DetourCreateProcessWithDlls(
    _In_opt_          LPCTSTR lpApplicationName,
    _Inout_opt_       LPTSTR lpCommandLine,
    _In_opt_          LPSECURITY_ATTRIBUTES lpProcessAttributes,
    _In_opt_          LPSECURITY_ATTRIBUTES lpThreadAttributes,
    _In_              BOOL bInheritHandles,
    _In_              DWORD dwCreationFlags,
    _In_opt_          LPVOID lpEnvironment,
    _In_opt_          LPCTSTR lpCurrentDirectory,
    _In_              LPSTARTUPINFOW lpStartupInfo,
    _Out_             LPPROCESS_INFORMATION lpProcessInformation,
    _In_              DWORD nDlls,
    _In_reads_(nDlls) LPCSTR *rlpDlls,
    _In_opt_          PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW
    );

启动程序并注入DLL

 

DetourCopyPayloadToProcess

BOOL DetourCopyPayloadToProcess(
    _In_                     HANDLE hProcess,  //进程句柄
    _In_                     REFGUID rguid,    //区段id
    _In_reads_bytes_(cbData) PVOID pvData,     //指向区段的指针
    _In_                     DWORD cbData      //区段的大小
    );

 

  • 7
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值