Detours 是微软开发的一个强大的 Windows API 钩子库,用于监视和拦截函数调用。它广泛应用于微软产品团队和众多独立软件开发中,旨在无需修改原始代码的情况下实现函数拦截和修改。Detours 在调试、监控、日志记录和性能分析等方面表现出色,已成为开发者的重要工具。本章将指导读者运用 Detours 库实现模块查询与枚举功能,帮助读者熟悉该库的使用技巧。
DetourFindFunction 该函数的主要功能是通过模块名称和函数名称来获取函数的地址,这对于在运行时动态加载模块并查找函数地址非常有用。
函数原型 其中参数一用于指定函数的模块名称、参数二则用于指定要查找的函数名称。
PVOID DetourFindFunction ( _In_ LPCSTR pszModule, _In_ LPCSTR pszFunction ) ;
我们可以通过使用 DetourFindFunction
获取自身进程内的 GetProcAddress
函数地址,并将其存储在 MyGetProcAddress
函数指针中。然后使用 LoadLibraryA
加载指定的动态链接库,并通过 MyGetProcAddress
函数指针获取任意模块中的函数地址。
使用示例 #include <windows.h> #include <iostream> #include "detours.h" #pragma comment(lib,"detours.lib" ) typedef FARPROC (WINAPI *GetProcAddress_t) (HMODULE hModule, LPCSTR lpProcName) ;int main (int argc, char *argv[]) { PVOID pFuncAddr = DetourFindFunction("kernel32.dll" , "GetProcAddress" ); if (pFuncAddr == NULL ) { return 0 ; } std ::cout << "GetProcAddress address: " << pFuncAddr << std ::endl ; GetProcAddress_t MyGetProcAddress = (GetProcAddress_t)pFuncAddr; HMODULE hModule = LoadLibraryA("user32.dll" ); if (hModule != NULL ) { FARPROC pMessageBoxA = MyGetProcAddress(hModule, "MessageBoxA" ); if (pMessageBoxA != NULL ) { std ::cout << "MessageBoxA address: " << pMessageBoxA << std ::endl ; } FreeLibrary(hModule); } system("pause" ); return 0 ; }
DetourCodeFromPointer 该函数的主要功能是处理可能的代码跳转或包装指针,并返回实际的代码入口点。这在处理被包装或钩子的代码时特别有用,因为它可以跳过钩子或包装层,直接获取原始代码的地址。
函数原型 其中参数一用于指定指向代码的指针,参数二则用于接收指向全局数据的指针。
PVOID DetourCodeFromPointer ( _In_ PVOID pPointer, _Out_opt_ PVOID* ppGlobals ) ;
使用示例 #include <windows.h> #include <iostream> #include "detours.h" #pragma comment(lib,"detours.lib" ) void SampleFunction () { std ::cout << "SampleFunction called." << std ::endl ; } int main (int argc, char *argv[]) { PVOID pFuncAddr = (PVOID)&SampleFunction; PVOID pCodeAddr = DetourCodeFromPointer(pFuncAddr, NULL ); std ::cout << "Original Function Address: " << pFuncAddr << std ::endl ; std ::cout << "Code Entry Point Address: " << pCodeAddr << std ::endl ; system("pause" ); return 0 ; }
DetourCopyInstruction 该函数的主要功能是拷贝给定地址的机器指令到目标地址,并处理指令中的相对地址(如跳转、调用)。这在实现代码拦截、跳转或重定向时非常有用,特别是在需要精确控制指令级别的操作时。
函数原型 其中参数一用于指定目标地址(即将指令拷贝到的地址),参数二用于存储指令的额外数据池地址,参数三用于指定源地址(即要拷贝的指令的地址),参数四用于接收指令中目标地址(跳转或调用的目标地址)的指针,参数五用于接收指令中额外数据大小的指针。
PVOID DetourCopyInstruction ( PVOID pDst, PVOID *ppDstPool, PVOID pSrc, PVOID *ppTarget, LONG *plExtra ) ;
使用示例 #include <windows.h> #include <iostream> #include "detours.h" #pragma comment(lib,"detours.lib" ) void SampleFunction () { std ::cout << "SampleFunction called." << std ::endl ; } int main (int argc, char *argv[]) { PVOID pSrc = (PVOID)&SampleFunction; BYTE buffer[16 ] = { 0 }; PVOID pDst = buffer; PVOID pNext = DetourCopyInstruction(pDst, NULL , pSrc, NULL , NULL ); std ::cout << "Source Address: " << pSrc << std ::endl ; std ::cout << "Next Instruction Address: " << pNext << std ::endl ; std ::cout << "Copied Instructions: " ; for (int i = 0 ; i < 16 ; i++) { std ::printf ("%02X " , buffer[i]); } std ::cout << std ::endl ; system("pause" ); return 0 ; }
DetourSetCodeModule 该函数的主要功能是设置指定代码模块的范围,以便 Detours 可以正确地处理代码拦截和重定向。这在多模块应用程序中非常有用,因为它允许你精确控制哪些代码可以被拦截或重定向。
函数原型 其中参数一用于指定要设置的代码模块的句柄,参数二则是一个布尔值,如果为 TRUE
,表示仅限于该模块中的引用;如果为 FALSE
,表示不限制引用。
BOOL WINAPI DetourSetCodeModule ( HMODULE hModule, BOOL fLimitReferencesToModule ) ;
通过这些步骤,你可以使用 DetourSetCodeModule
设置代码模块的范围,从而精确控制哪些代码可以被 Detours 拦截或重定向。
使用示例 #include <windows.h> #include <iostream> #include "detours.h" #pragma comment(lib,"detours.lib" ) int main (int argc, char *argv[]) { HMODULE hModule = GetModuleHandle(NULL ); BOOL result = DetourSetCodeModule(hModule, TRUE); if (result) { std ::cout << "Successfully set code module." << std ::endl ; } else { std ::cerr << "Failed to set code module." << std ::endl ; return 1 ; } system("pause" ); return 0 ; }
DetourGetContainingModule 该函数的主要功能是查找包含指定地址的模块的句柄。这在进行代码拦截和重定向时非常有用,因为它允许你确定特定函数或代码段所在的模块。
函数原型 该函数仅需要传入一个参数,即一个指向内存地址的指针,表示要查找其所属模块的地址。
HMODULE WINAPI DetourGetContainingModule (PVOID pvAddr) ;
使用示例 #include <windows.h> #include <iostream> #include "detours.h" #pragma comment(lib,"detours.lib" ) void SampleFunction () { std ::cout << "SampleFunction called." << std ::endl ; } int main (int argc, char *argv[]) { PVOID pFuncAddr = (PVOID)&SampleFunction; HMODULE hModule = DetourGetContainingModule(pFuncAddr); if (hModule != NULL ) { char moduleName[MAX_PATH] = { 0 }; if (GetModuleFileNameA(hModule, moduleName, sizeof (moduleName))) { std ::cout << "Module containing SampleFunction: " << moduleName << std ::endl ; } else { std ::cerr << "Failed to get module file name." << std ::endl ; } } else { std ::cerr << "Failed to find containing module." << std ::endl ; return 1 ; } system("pause" ); return 0 ; }
DetourEnumerateModules 该函数的主要功能是遍历当前进程中的所有模块,通过反复调用该函数并传入上一个模块的句柄,你可以枚举当前进程中的所有模块。
函数原型 该函数仅需要传入一个参数,即上一个模块的句柄。如果是第一次调用该函数,应传入 NULL
。
HMODULE WINAPI DetourEnumerateModules (HMODULE hModuleLast) ;
该函数通常可配合 DetourGetEntryPoint
及 DetourGetModuleSize
一起使用,通过三个函数的配合,则可获取到当前进程中模块名、模块入口点及模块大小信息。
使用示例 #include <windows.h> #include <iostream> #include "detours.h" #pragma comment(lib,"detours.lib" ) int main (int argc, char *argv[]) { HMODULE hModule = NULL ; while ((hModule = DetourEnumerateModules(hModule)) != NULL ) { char moduleName[MAX_PATH] = { 0 }; if (GetModuleFileNameA(hModule, moduleName, sizeof (moduleName))) { std ::cout << "Module: " << moduleName << std ::endl ; } PVOID pEntryPoint = DetourGetEntryPoint(hModule); if (pEntryPoint != NULL ) { std ::cout << "Entry point address: " << pEntryPoint << std ::endl ; } DWORD moduleSize = DetourGetModuleSize(hModule); if (moduleSize != 0 ) { std ::cout << "Module size: " << moduleSize << " bytes." << std ::endl ; } } system("pause" ); return 0 ; }
DetourEnumerateExports 该函数的主要功能是枚举指定模块中的所有导出函数,并对每个导出函数调用指定的回调函数。回调函数可以用于处理或操作每个导出函数。
函数原型 其中参数一用于指定要枚举的模块的句柄,参数二用于传递给回调函数的上下文指针,可以是任何类型的数据,通常用于传递状态信息。参数三则指向回调函数的指针,该回调函数在每个导出函数上调用。
BOOL WINAPI DetourEnumerateExports ( HMODULE hModule, PVOID pContext, PF_DETOUR_ENUMERATE_EXPORT_CALLBACK pfExportCallback ) ;
在回调函数中,参数一用于传递给 DetourEnumerateExports
的上下文指针。参数二指定导出函数的序号。参数三指定导出函数的名称。参数四指定导出函数的地址。
typedef BOOL (CALLBACK *PF_DETOUR_ENUMERATE_EXPORT_CALLBACK) ( PVOID pContext, ULONG nOrdinal, LPCSTR pszName, PVOID pCode ) ;
使用示例 #include <windows.h> #include <iostream> #include "detours.h" #pragma comment(lib,"detours.lib" ) BOOL CALLBACK ExportCallback (PVOID pContext, ULONG nOrdinal, LPCSTR pszName, PVOID pCode) { std ::cout << "Ordinal: " << nOrdinal << ", Name: " << (pszName ? pszName : "(unnamed)" ) << ", Address: " << pCode << std ::endl ; return TRUE; } int main (int argc, char *argv[]) { HMODULE hModule = GetModuleHandle(NULL ); if (hModule == NULL ) { std ::cerr << "Failed to get module handle." << std ::endl ; return 1 ; } if (!DetourEnumerateExports(hModule, NULL , ExportCallback)) { std ::cerr << "Failed to enumerate exports." << std ::endl ; return 1 ; } system("pause" ); return 0 ; }
DetourEnumerateImports 该函数的主要功能是枚举指定模块中的所有导入函数,并对每个导入模块和导入函数调用指定的回调函数。回调函数可以用于处理或操作每个导入模块和导入函数。
函数原型 参数一用于指定要枚举的模块句柄,参数二指定回调函数上下文指针,参数三指定回调函数指针(该回调函数在每个导入模块上调用),参数四指定回调函数指针(该回调函数在每个导入函数上调用)。
BOOL WINAPI DetourEnumerateImports ( HMODULE hModule, PVOID pContext, PF_DETOUR_IMPORT_FILE_CALLBACK pfImportFileCallback, PF_DETOUR_IMPORT_FUNC_CALLBACK pfImportFuncCallback ) ;
在文件回调函数中,参数一用于传入上下文指针,参数二传递模块名称。
typedef BOOL (CALLBACK *PF_DETOUR_IMPORT_FILE_CALLBACK) ( PVOID pContext, LPCSTR pszFile ) ;
在函数回调函数中,参数一用于传入上下文指针,参数二为导入函数的序号,参数三为导入函数的名称,参数四为指向导入函数地址的指针。
typedef BOOL (CALLBACK *PF_DETOUR_IMPORT_FUNC_CALLBACK) ( PVOID pContext, DWORD nOrdinal, LPCSTR pszFunc, PVOID *ppvFunc ) ;
使用示例 #include <windows.h> #include <iostream> #include "detours.h" #pragma comment(lib, "detours.lib" ) BOOL CALLBACK ImportFileCallback (PVOID pContext, LPCSTR pszFile) { std ::cout << "Import Module: " << pszFile << std ::endl ; return TRUE; } BOOL CALLBACK ImportFuncCallback (PVOID pContext, DWORD nOrdinal, LPCSTR pszFunc, PVOID *ppvFunc) { std ::cout << " Ordinal: " << nOrdinal << ", Name: " << (pszFunc ? pszFunc : "(unnamed)" ) << ", Address: " << *ppvFunc << std ::endl ; return TRUE; } int main (int argc, char *argv[]) { HMODULE hModule = GetModuleHandle(NULL ); if (hModule == NULL ) { return 1 ; } if (!DetourEnumerateImports(hModule, NULL , (PF_DETOUR_IMPORT_FILE_CALLBACK)ImportFileCallback, (PF_DETOUR_IMPORT_FUNC_CALLBACK)ImportFuncCallback)) { return 1 ; } system("pause" ); return 0 ; }