概述:Detours 的说明和简单使用

Detours

Detours 是微软提供的一套工具,主要用于win32 API的拦截

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

仓库中有挺多示例程序可供参考。

使用 Detours 的大致流程:

在 Detours 的工作过程中,DetourTransactionBegin 函数首先用于启动一个截获(Hook)或解除截获(UnHook)的过程,这个过程中可能包含对多个函数的修改。然后,开发者可以使用 DetourAttachDetourAttachEx 函数将 Detour 钩子附加到目标函数上,或使用 DetourDetach 函数将钩子从目标函数中分离出来。

相关函数说明

DetourUpdateThread 函数说明

DetourUpdateThread 函数是 Detours 库中的一个重要函数,用于在 Detour 事务处理过程中将特定的线程列入更新范围。Detours 是一个用于在运行时修改二进制代码(如函数钩子)的库,它允许开发者在不修改原始代码的情况下,拦截和修改函数的执行流程。

DetourUpdateThread 函数的作用是在这个事务过程中,将一个或多个线程列入需要更新的范围。这是因为在多线程环境中,直接修改函数的代码可能会导致竞态条件,即一个线程正在执行原始代码时,另一个线程可能已经修改了该代码。通过 DetourUpdateThread 将线程列入更新范围,Detours 库可以确保在事务提交时,所有相关线程的代码都被正确地更新,从而避免竞态条件。

具体来说,DetourUpdateThread 函数的作用是:

  1. 将指定的线程加入到 Detour 事务的更新列表中。
  2. 在事务提交时,Detours 库会遍历这个更新列表,并对列表中的每个线程执行必要的代码更新操作。

需要注意的是,在调用 DetourTransactionCommitDetourTransactionCommitEx 函数提交事务之前,所有的附加、分离和线程更新操作都不会生效。一旦事务被提交,所有的修改就会立即生效,所有相关线程的代码都会被更新以反映这些修改。

因此,DetourUpdateThread 函数是 Detours 库中用于确保多线程环境下代码修改一致性和安全性的重要工具之一。

可以看下 DetourUpdateThread 的源码,如下所示:

LONG WINAPI DetourUpdateThread(_In_ HANDLE hThread)
{
    LONG error;
 
    // If any of the pending operations failed, then we don't need to do this.
    if (s_nPendingError != NO_ERROR) {
        return s_nPendingError;
    }
 
    // Silently (and safely) drop any attempt to suspend our own thread.
    if (hThread == GetCurrentThread()) {
        return NO_ERROR;
    }
 
    DetourThread *t = new NOTHROW DetourThread;
    if (t == NULL) {
        error = ERROR_NOT_ENOUGH_MEMORY;
      fail:
        if (t != NULL) {
            delete t;
            t = NULL;
        }
        s_nPendingError = error;
        s_ppPendingError = NULL;
        DETOUR_BREAK();
        return error;
    }
 
    if (SuspendThread(hThread) == (DWORD)-1) {
        error = GetLastError();
        DETOUR_BREAK();
        goto fail;
    }
 
    t->hThread = hThread;
    t->pNext = s_pPendingThreads;
    s_pPendingThreads = t;
 
    return NO_ERROR;
}
 

hookDemo

修改 GetLocalTime ,使其返回的分钟数为 52

#include <iostream>
#include <Windows.h>
//包含Detour的头文件和库文件
#include "detours.h"
#pragma comment (lib,"detours.lib")
using namespace std;
 
//保存函数原型(用指针存储要拦截的API)
void (*OldGetLocalTime)(LPSYSTEMTIME) = GetLocalTime;
 
//拦截后要执行的操作(这里是将时间的分改为52)
void NewGetLocalTime(LPSYSTEMTIME lpSystemTime) {
	OldGetLocalTime(lpSystemTime);
	lpSystemTime->wMinute = 52;
}
 
//下钩子函数
void StartHook() {
	//开始事务
	DetourTransactionBegin();
	//更新线程信息
	DetourUpdateThread(GetCurrentThread());
	//将拦截的函数附加到原函数的地址上
	DetourAttach(&(PVOID&)OldGetLocalTime, NewGetLocalTime);
	//结束事务
	DetourTransactionCommit();
}
 
//撤钩子函数
void EndHook() {
	//开始detours事务
	DetourTransactionBegin();
	//更新线程信息 
	DetourUpdateThread(GetCurrentThread());
	//将拦截的函数从原函数的地址上解除
	DetourDetach(&(PVOID&)OldGetLocalTime, NewGetLocalTime);
	//结束detours事务
	DetourTransactionCommit();
}
 
int main()
{
	//获取本地时间
	SYSTEMTIME time, time2;
	GetLocalTime(&time);
	cout << time.wHour << ":" << time.wMinute << endl;
	//下钩子
	StartHook();
 
	//下钩子后再次获取本地时间
	GetLocalTime(&time2);
	cout << time2.wHour << ":" << time2.wMinute << endl;
 
	//撤钩子
	EndHook();
	return 0;
}