Detours库Windows API Hook

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

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

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

从GitHub上下载源码

GitHub - microsoft/Detours: Detours is a software package for monitoring and instrumenting API calls on Windows. It is distributed in source code form.

1、电脑中装了 VS2019或者更高,所以会有这个下图中的这个命令行程序,要以管理员身份运行
2、找到x64 Native Tools Command Prompt for VS 2019,x86 Native Tools Command Prompt for VS 2019,版本不同生成不同
3、先使用 cd 命令切换到所下载的源码目录下 (如果不是以管理员身份运行的,你将切换失败) :cd 你的路径
4、然后使用命令编译即可:nmake /f makefile
5、编译成功,会有集合文件夹

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()
{
	//恢复之前状态,避免反复拦截
	DetourRestoreAfterWith();
    //开始事务
    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 HOOK函数

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

DetourAttachEx  HOOK函数

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.
};

DetourDetach  取消HOOK

LONG DetourDetach(
    _Inout_ PVOID * ppPointer,  //被HOOK函数地址的二级指针
    _In_    PVOID pDetour        //替换函数的地址
    );
用于查找目标函数的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 定位二进制文件中映射的payloads

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

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

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

DetourGetSizeOfPayloads 获取Payload的大小  

DWORD DetourGetSizeOfPayloads(
    _In_ HMODULE hModule
    );
修改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  启动程序并注入DLL

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。
    );

 DetourCreateProcessWithDlls 启动程序并注入DLL

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
    );

DetourCopyPayloadToProcess

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值