Detours入门

@lzeroyuee  November 15, 2020

简介

Microsoft Research Detours Package

Detours是微软官方的一个Hook库,该库可直接使用vs进行编译,使用的是inline hook

Detours的使用

利用Detours库进行Hook时,只需要提供三个信息:

  1. 原函数的原型信息(汇编级别的原型就行:参数个数、大小、调用约定、返回类型大小)
  2. 原函数的地址
  3. Hook函数的地址

Detours库的使用主要为以下两部分:

  • Hook

    • DetourRestoreAfterWith,恢复之前的状态
    • DetourTransactionBegin,开始事物
    • DetourUpdateThread,刷新当前线程
    • DetourAttach,设置Hook
    • DetourTransactionCommit,提交事物
  • UnHook

    • DetourTransactionBegin,开始事物
    • DetourUpdateThread,刷新当前线程
    • DetourDetach,设置UnHook
    • DetourTransactionCommit,提交事物

Hook 自定义C函数

  1. 确定目标函数地址,硬编码、内存搜索都可以
  2. 确定目标函数原型,没有源码的时候逆向就行,原型只要求在汇编级别上一致即可
  3. 在DLL中编写Hook函数,完成Hook
/********************************** TestProject.exe **********************************/
#include <stdio.h>
#include <Windows.h>

int foo(int a, int b)
{
    printf("foo func: %d\n", a + b);
    return a + b;
}

int main(void)
{
    printf("num = %d\n", foo(1, 2));
    system("pause");

    // Hook
    HMODULE hmodule = LoadLibraryA("HookTest.dll");        // 加载DLL来模拟注入
    printf("num = %d\n", foo(1, 2));
    FreeLibrary(hmodule);
    system("pause");

    // UnHook
    printf("num = %d\n", foo(1, 2));

    return 0;
}

/********************************** HookTest.dll **********************************/
#include "D:/Third-party Library/Detours/include/detours.h"

#ifdef _WIN64
#pragma comment(lib, "D:/Third-party Library/Detours/lib.X64/detours.lib")
#else
#pragma comment(lib, "D:/Third-party Library/Detours/lib.X86/detours.lib")
#endif

// 目标函数指针
typedef int (*FOO_PTR)(int a, int b);
FOO_PTR foo_ptr = (FOO_PTR)0x00411780;        // 目标函数地址

// 定义Hook函数
int hook_foo(int a, int b)
{
    a *= 10;
    b += 5;
    return foo_ptr(a, b);
}

void start_hook()
{
    DetourRestoreAfterWith();                   // 恢复之前的状态,防止重复Hook
    DetourTransactionBegin();                   // 开始事物
    DetourUpdateThread(GetCurrentThread());     // 刷新当前线程
    DetourAttach(&(void *&)foo_ptr, hook_foo);  // 设置Hook
    DetourTransactionCommit();                  // 提交事物
}


void stop_hook()
{
    DetourTransactionBegin();                   // 开始事物
    DetourUpdateThread(GetCurrentThread());     // 刷新当前线程
    DetourDetach((void **)&foo_ptr, hook_foo);  // 设置UnHook
    DetourTransactionCommit();                  // 提交事物
}


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

1.png

Hook 类成员函数

DetourAttach函数的第二个参数类型为PVOID,在高版本的编译器中,对类成员函数强转有很严格的要求,基本上不可能转为PVOID,那么就需要换一种思路,利用静态成员函数来完成,自己保存this指针,必要时使用裸函数__declspec(naked),自己平栈

针对前例的修改如下:

/********************************** TestProject.exe **********************************/
class Test {
public:
    int bar(int n)
    {
        printf("bar func: %d\n", n);
        return n;
    }
};

int main(void)
{
    Test test;
    test.bar(1);
    system("pause");

    // Hook
    HMODULE hmodule = LoadLibraryA("HookTest.dll");
    test.bar(1);
    FreeLibrary(hmodule);
    system("pause");

    // UnHook
    test.bar(1);

    return 0;
}


/********************************** HookTest.dll **********************************/
class HookTest {
public:
    static int hook_bar(int n)
    {
        __asm push ecx
        n += 100;
        //return (this->*bar_ptr)(n);
        ULONG_PTR this_obj;
        __asm {
            pop eax
            mov this_obj, eax
        }
        return (((HookTest *)this_obj)->*bar_ptr)(n);
    }

    static int (HookTest::*bar_ptr)(int n);     // 类成员函数指针
};

int (HookTest::*HookTest::bar_ptr)(int n) = NULL;    // 初始化静态成员函数指针,后续在赋值
ULONG_PTR target = 0x0401080;        // 目标函数地址

void start_hook()
{
    ...
    DetourAttach((void **)&HookTest::bar_ptr, (void *)HookTest::hook_bar);  // 设置Hook
    ...
}


void stop_hook()
{
    ...
    DetourDetach((void **)&HookTest::bar_ptr, (void *)HookTest::hook_bar);  // 设置UnHook
    ...
}


BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        memcpy(&HookTest::bar_ptr, &target, sizeof(target));
        start_hook();
        break;
    ...
    }
    return TRUE;
}

2.png

Hook API

可以使用DetourFindFunction函数来获取API的地址,同时DetourCreateProcessWithDll函数可以创建进程并注入DLL,其原理也就是调用CreateProcess,创建标志给上CREATE_SUSPENDED,以暂停进程,之后进行注入

注意:使用DetourCreateProcessWithDll函数,目标DLL必须要有至少一个导出函数

/********************************** TestProject.exe **********************************/
#include <stdio.h>
#include <Windows.h>

#include "D:/Third-party Library/Detours/include/detours.h"

#ifdef _WIN64
#pragma comment(lib, "D:/Third-party Library/Detours/lib.X64/detours.lib")
#else
#pragma comment(lib, "D:/Third-party Library/Detours/lib.X86/detours.lib")
#endif

int main(void)
{
    TCHAR proc_name[256] = L"notepad.exe";
    char dll_name[256] = "E:\\WorkSpace\\HookTest\\HookTest\\Release\\HookTest.dll";
    STARTUPINFO si { 0 };
    PROCESS_INFORMATION pi { 0 };
    si.cb = sizeof(si);
    DetourCreateProcessWithDll(NULL, proc_name, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi, dll_name, NULL);
    WaitForSingleObject(pi.hProcess, INFINITE);
    return 0;
}


/********************************** HookTest.dll **********************************/
HOOKTEST_API BOOL MyWriteFile(
    HANDLE       hFile,
    LPCVOID      lpBuffer,
    DWORD        nNumberOfBytesToWrite,
    LPDWORD      lpNumberOfBytesWritten,
    LPOVERLAPPED lpOverlapped
)
{
    hFile = INVALID_HANDLE_VALUE;
    return WriteFile(hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped);
}

void start_hook()
{
    DetourRestoreAfterWith();                   // 恢复之前的状态,防止重复Hook
    DetourTransactionBegin();                   // 开始事物
    DetourUpdateThread(GetCurrentThread());     // 刷新当前线程
    PVOID write_file_ptr = DetourFindFunction("kernel32.dll", "WriteFile");     // 查找WriteFile地址
    DetourAttach(&write_file_ptr, MyWriteFile);  // 设置Hook
    DetourTransactionCommit();                  // 提交事物
}


void stop_hook()
{
    DetourTransactionBegin();                   // 开始事物
    DetourUpdateThread(GetCurrentThread());     // 刷新当前线程
    PVOID write_file_ptr = DetourFindFunction("kernel32.dll", "WriteFile");     // 查找WriteFile地址
    DetourDetach(&write_file_ptr, MyWriteFile);  // 设置UnHook
    DetourTransactionCommit();                  // 提交事物
}

添加新评论