ZLUDA 中的 Microsoft Detours(ext/detours):Windows API Hook 的原理、构建与工程化集成
【免费下载链接】ZLUDACUDA on non-NVIDIA GPUs项目地址: https://gitcode.com/GitHub_Trending/zl/ZLUDA
本篇围绕仓库中引入的ext/detours目录(Microsoft Research Detours Package v4.0.1)展开,覆盖其定位、许可与兼容性边界、官方示例的构建方式,并结合 ZLUDA 仓库中detours-sys、zluda_redirect等 crate 的源码,说明这套 Hook 库是如何被编译进 Rust 生态并实际用于 Windows 进程注入与 DLL 重定向的。读完后你能理解 Detours 的核心 API 模型、在本仓库中的集成链路,以及如何独立构建其示例与测试。
一、Detours 是什么,为什么 ZLUDA 要引入它
根据 ext/detours/README.md 的描述,Detours 是一个用于在 Windows 上监控和插桩(instrumenting)API 调用的软件包,长期被众多 ISV 以及微软的产品团队使用。该 README 明确了几个关键事实:
- Detours 当前以标准开源许可发布(MIT),简化了下游使用者的授权流程;
- 它兼容 Windows NT 家族的操作系统:Windows NT、Windows XP、Windows Server 2003、Windows 7、Windows 8 与 Windows 10;
- 它不能用于 Windows Store 应用,因为 Detours 依赖了这些应用无法访问的 API;
- 本仓库包含的是Detours 4.0.1版本的源码。
对 ZLUDA("CUDA on non-NVIDIA GPUs")而言,Windows 侧的一个核心诉求是:让原本调用 NVIDIA 驱动库(cuDLL)的进程改为加载 ZLUDA 的替代实现。这必须依赖进程级 API 拦截——拦截LoadLibrary*、CreateProcess*等系统调用并在其中改写行为。Detours 提供了在 x86/x64 代码流中安全重写指令前缀、建立跳板(trampoline)的能力,是这类"透明替换 DLL"方案的基础设施。
从仓库结构看,Detours 源码整体 vendored 在 ext/detours 下,包含src/(核心实现)、samples/(官方示例集)、tests/(模块与映像 API 的 C++ 测试)、vc/(Visual Studio 工程文件)以及 LICENSE.md、CREDITS.TXT。核心 C++ 实现入口在 ext/detours/src/detours.cpp,对外接口声明集中在 ext/detours/src/detours.h。
二、构建环境与官方示例:从 samples/README.TXT 继承的完整操作路径
ZLUDA 的 README 指向了 Detours 官方的 samples 文档,对应本仓库中的 ext/detours/samples/README.TXT。其中的构建流程是完整可执行的实操路径,这里完整保留并整理:
2.1 构建环境准备
- 安装 Visual Studio IDE,并确保安装了C/C++ 工具与Windows SDK;
- 将 Detours 源码放置到一个完整路径中不含空格的目录(本仓库中即位于
ext/detours)。
2.2 构建步骤
- 打开 Developer Command Prompt for VS。注意有多种架构变体:默认的命令提示符目标是 x86;若要构建 x64 目标,应选择 "X64 Native Tools Command Prompt for VS";
- 切换到 samples 目录;
- 执行
nmake构建全部示例; - 注意:
setdll和syelog必须先构建成功,许多其他示例依赖它们。
2.3 测试步骤
- 每个示例目录自带测试,执行
nmake test即可演示该示例的用法; - 几乎所有可执行文件都支持
/?参数打印用法说明; - 在 samples 根目录执行
nmake test可运行全部测试;部分示例是架构相关的,仅在其支持的架构上运行,其他架构会被跳过。
2.4 通过 vcpkg 安装(外部使用场景)
官方 README 也给出了用 vcpkg 依赖管理器安装 detours 的路径:
git clone <vcpkg 仓库地址> cd vcpkg ./bootstrap-vcpkg.sh ./vcpkg integrate install vcpkg install detours2.5 trace* 示例的运行模式
samples README 的最后一段解释了一个重要的运行特征:trace*系列示例通过syelogd.exe守护进程记录日志,并 hookCreateProcessW以把自身注入到所有子进程中。例如执行withdll -d:traceapi.dll cmd.exe会创建一个命令 shell,其下所有进程都会通过traceapi.dll记录 API 调用。这正是 Detours "随进程树扩散"能力的典型演示,也是理解 ZLUDA 注入行为的参照系。
三、核心 API 面:事务模型、进程注入与二进制编辑
从 ext/detours/src/detours.h 的声明(约 L539–L823)可以完整梳理 Detours 的 API 家族,按功能分为四组:
3.1 Hook 事务(Transaction)
LONG WINAPI DetourTransactionBegin(VOID); LONG WINAPI DetourTransactionAbort(VOID); LONG WINAPI DetourTransactionCommit(VOID); LONG WINAPI DetourTransactionCommitEx(_Out_opt_ PVOID **pppFailedPointer); LONG WINAPI DetourAttach(_Inout_ PVOID *ppPointer, _In_ PVOID pDetour); LONG WINAPI DetourDetach(_Inout_ PVOID *ppPointer, _In_ PVOID pDetour); LONG WINAPI DetourUpdateThread(_In_ HANDLE hThread);这是 Detours 的标志性设计:所有 Hook 的挂接/卸载都发生在DetourTransactionBegin与DetourTransactionCommit之间的事务中,DetourAttach将一个目标函数指针(ppPointer)替换为替换函数(pDetour),DetourUpdateThread则把 hook 应用到指定线程。事务化保证了多个 hook 要么全部生效、要么整体回滚,避免进程停留在半改写状态。配套的开关如DetourSetIgnoreTooSmall、DetourSetRetainRegions、DetourSetSystemRegionLowerBound/UpperBound用于控制代码区改写策略。
3.2 二进制编辑(离线修改 PE 文件)
PDETOUR_BINARY WINAPI DetourBinaryOpen(_In_ HANDLE hFile); BOOL WINAPI DetourBinaryEditImports(_In_ PDETOUR_BINARY pBinary, ...); BOOL WINAPI DetourBinaryWrite(_In_ PDETOUR_BINARY pBinary, _In_ HANDLE hFile); BOOL WINAPI DetourBinaryClose(_In_ PDETOUR_BINARY pBinary);这一组 API 允许在不运行目标程序的情况下打开一个 PE 文件、编辑其导入表、嵌入/读取 GUID 负载(payload)后写回。这为"预先改写二进制再运行"的部署方式提供了底层能力。
3.3 进程创建与动态注入
BOOL WINAPI DetourCreateProcessWithDllW(_In_opt_ LPCWSTR lpApplicationName, ...); BOOL WINAPI DetourCreateProcessWithDllsW(_In_opt_ LPCWSTR lpApplicationName, ...); BOOL WINAPI DetourUpdateProcessWithDll(_In_ HANDLE hProcess, ...); BOOL WINAPI DetourCopyPayloadToProcess(_In_ HANDLE hProcess, ...); BOOL WINAPI DetourRestoreAfterWith(VOID); BOOL WINAPI DetourIsHelperProcess(VOID);DetourCreateProcessWithDll(W):创建新进程的同时把指定 DLL 注入其中;DetourUpdateProcessWithDll:对已运行的进程注入 DLL;DetourCopyPayloadToProcess:向目标进程传送自定义二进制负载(例如覆盖用的 DLL 字节),配合 payload 机制使用;DetourIsHelperProcess/DetourRestoreAfterWith:Detours 内部借助"helper 进程"完成跨进程改写,这两个 API 让注入的 DLL 能够识别自己是否处于 helper 上下文中并在恢复时正确清理。
3.4 模块与代码工具函数
DetourFindFunction、DetourGetContainingModule、DetourEnumerateModules、DetourGetEntryPoint、DetourGetModuleSize、DetourEnumerateImports(Ex)、DetourBinaryFindPayload(Ex)等,用于在运行时枚举模块、定位导出函数与导入表,是编写 hook 前"搞清楚要 hook 什么"的基础工具。
四、ZLUDA 如何编译并使用这份 vendored 源码
4.1 detours-sys:Rust 侧的 FFI 绑定与自动编译
仓库中的 detours-sys/Cargo.toml 声明了一个links = "detours"的 FFI 绑定 crate(关键词包括hooking、injection),其构建脚本 detours-sys/build.rs 展示了 vendored 源码的实际编译方式:
- 直接编译
ext/detours/src下的 5 个核心 C++ 文件:creatwth.cpp(进程注入辅助)、detours.cpp(核心 hook 引擎)、disasm.cpp(指令反汇编,用于跳板改写)、image.cpp(映像操作)、modules.cpp(模块枚举); - 针对 MSVC 目标(
CARGO_CFG_TARGET_ENV == "msvc")走原生 MSVC 编译路径;否则回退到clang +-fms-extensions以兼容 MinGW/Clang 环境,并加-Wno-everything抑制告警; - 编译产物通过
try_compile("detours")自动链接进最终 crate。
绑定头文件通过 bindgen 一次性生成并内嵌为 detours-sys/src/bundled_bindings.rs(detours-sys/src/lib.rs 第 7–9 行的注释保留了生成命令,--whitelist-function "Detour.*"表明只导出Detour*系列符号)。
4.2 一个最小可运行的 Hook 测试
detours-sys/src/lib.rs 自带一个hook_self测试,它完整演示了 Detours 事务模型的正确姿势,值得作为最小范例:
// 1) 保存原始 Sleep 函数指针 static mut TRUE_SLEEP: unsafe extern "system" fn(DWORD) = Sleep; // 2) 替换函数:调用前记录 GetTickCount(),再调用原 Sleep,记录实际耗时 unsafe extern "system" fn TimedSleep(dwMilliseconds: DWORD) { ... } // 3) 在 DllMain 的 DLL_PROCESS_ATTACH 中挂接 DetourRestoreAfterWith(); // 先清理可能残留的 helper 状态 DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread() as _); DetourAttach(tru, new); // 把 Sleep 替换为 TimedSleep DetourTransactionCommit(); // 4) DLL_PROCESS_DETACH 中对称地 DetourDetach 恢复测试随后调用Sleep(500)验证替换生效(SLEPT != 0),恢复后再调用Sleep验证已还原(slept == SLEPT语义上的对比断言)。这段测试同时印证了 Detours 的对称性纪律:DetourRestoreAfterWith必须在挂接前调用,且 attach/detach 需要镜像执行——这是使用 Detours 编写注入 DLL 时必须遵守的基本契约。
4.3 zluda_redirect:Detours 在 ZLUDA Windows 链路中的真实用途
zluda_redirect/Cargo.toml 显示zluda_redirect是一个仅 Windows 可用、32 位的cdylib(即会被加载进目标进程的 DLL),依赖detours-sys与zluda_windows。其实现 zluda_redirect/src/lib.rs 从detours_sys中直接导入了一批符号:
use detours_sys::{ DetourAttach, DetourCopyPayloadToProcess, DetourDetach, DetourRestoreAfterWith, DetourTransactionAbort, DetourTransactionBegin, DetourTransactionCommit, DetourUpdateProcessWithDll, DetourUpdateThread, LPCWSTR, };结合其导入的 Windows API 列表(CreateProcessA/W、CreateProcessAsUserA/W、CreateProcessWithLogonW、CreateProcessWithTokenW、LoadLibraryA/W、LoadLibraryExA/W,见同文件 L29–L39 的use声明),可以推断zluda_redirect的工作方式与 samples 中withdll/trace*的模式一致:用 Detours 事务挂接进程内所有创建子进程与加载 DLL 的入口,在加载路径命中 CUDA 相关库名时改写为 ZLUDA 提供的实现(文件中的DetourPaths结构维护了按 GUID 编码的 DLL 路径覆盖表override_paths,与zluda_windows::LIBRARIES一一对应),并利用DetourUpdateProcessWithDll+DetourCopyPayloadToProcess将重定向 DLL 传播到子进程。这与 zluda_windows(Windows 侧加载入口)和detours-sys共同构成了 ZLUDA 在 Windows 上的"透明替换驱动 DLL"基础设施。
五、许可与版本边界
ext/detours目录下的代码遵循 MIT 许可(见 ext/detours/LICENSE.md 与 README 的 License 小节);仓库自身的detours-sys等 crate 采用 Apache-2.0 / MIT 双许可(见 detours-sys/Cargo.toml 的license字段)。- 版本事实以仓库为准:vendored 的是4.0.1;兼容边界为 Windows NT 家族且明确排除 Windows Store 应用。
- 若在非 MSVC 工具链上构建
detours-sys,build.rs会强制要求 Clang 并启用 MS 扩展语法(-fms-extensions),这一点在跨工具链移植时需要提前准备。
六、小结
ext/detours在 ZLUDA 仓库中并非孤立的历史代码,而是一条可验证的完整集成链:vendored 的 Detours 4.0.1 源码 →detours-sys的 build.rs 自动编译五个核心 cpp 文件并导出Detour*FFI 符号 →zluda_redirect以 cdylib 形式利用事务化DetourAttach挂接进程创建与 DLL 加载 API,把 CUDA 库调用透明重定向到 ZLUDA。构建官方示例时以 ext/detours/samples/README.TXT 中的 nmake 流程为准,理解注入与 payload 机制时可参照 samples 中的withdll、traceapi以及 detours-sys/src/lib.rs 的hook_self测试。
【免费下载链接】ZLUDACUDA on non-NVIDIA GPUs项目地址: https://gitcode.com/GitHub_Trending/zl/ZLUDA
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考