使用MS Detours挂钩NtWriteFile时调用原函数卡顿问题求助
问题描述
使用MS Detours工具挂钩NtWriteFile函数,编写对应DLL并计划通过withdll.exe加载。调试确认MyNtWriteFile已被正常调用,但执行原函数RealNtWriteFile时出现卡顿,求排查思路。
精简DLL代码
#include "pch.h" #include<windows.h> #include <detours.h> #include <stdio.h> #include <iostream> #include <winternl.h> typedef NTSTATUS(*NtWriteFileFunc)( HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, PVOID Buffer, ULONG Length, PLARGE_INTEGER ByteOffset, PULONG Key ); NTSTATUS WINAPI MyNtWriteFile( HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, PVOID Buffer, ULONG Length, PLARGE_INTEGER ByteOffset, PULONG Key ) { // Call the original function. NtWriteFileFunc RealNtWriteFile = (NtWriteFileFunc)GetProcAddress(LoadLibrary(L"ntdll.dll"), "NtWriteFile"); NTSTATUS tmp = RealNtWriteFile(FileHandle, Event, ApcRoutine, ApcContext, IoStatusBlock, Buffer, Length, ByteOffset, Key); return tmp; } BOOL WINAPI DllMain(HINSTANCE hinst, DWORD dwReason, LPVOID reserved) { HMODULE hNtdll = LoadLibrary(L"ntdll.dll"); NtWriteFileFunc RealNtWriteFile = (NtWriteFileFunc)GetProcAddress(hNtdll, "NtWriteFile"); LONG error; if (DetourIsHelperProcess()) { return TRUE; } if (dwReason == DLL_PROCESS_ATTACH) { DetourRestoreAfterWith(); DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); DetourAttach(&(PVOID&)RealNtWriteFile, MyNtWriteFile); error = DetourTransactionCommit(); } else if (dwReason == DLL_PROCESS_DETACH) { DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); DetourDetach(&(PVOID&)RealNtWriteFile, MyNtWriteFile); error = DetourTransactionCommit(); } return TRUE; }
排查思路
- 修复原函数地址的获取逻辑:当前
MyNtWriteFile每次调用都重新执行LoadLibrary+GetProcAddress,不仅低效,还可能因重复加载模块导致地址异常。应在DLL_PROCESS_ATTACH阶段一次性获取原函数地址,保存为全局变量,钩子函数直接调用该全局变量。 - 修正DetourAttach的参数错误:DllMain中的
RealNtWriteFile是局部变量,DetourAttach修改的只是这个局部变量的地址,并未真正完成全局挂钩。需将RealNtWriteFile定义为全局变量,确保Detour能正确修改它指向跳转后的地址,避免钩子逻辑混乱引发阻塞。 - 完善线程上下文更新:当前仅更新了当前线程的上下文(
DetourUpdateThread(GetCurrentThread())),其他线程可能仍在使用旧函数地址,引发线程冲突或阻塞。建议挂钩时遍历目标进程的所有线程并更新上下文。 - 规避DllMain中的风险操作:在DllMain中调用
LoadLibrary存在触发加载锁死的风险,应将ntdll的加载和原函数指针获取移到DLL_PROCESS_ATTACH内的安全执行区域,或提前通过全局初始化完成。 - 校验调用约定与参数匹配:确认
NtWriteFile的调用约定(原生为__stdcall即WINAPI)和参数类型完全匹配,比如IO_STATUS_BLOCK的结构定义是否准确,参数不匹配可能导致栈溢出或调用异常,表现为卡顿。 - 定位原函数卡顿点:用调试器中断在
RealNtWriteFile调用处,单步跟进原函数执行流程,确认卡顿发生在系统调用入口、APC回调还是IO等待环节,精准定位问题根源。
内容的提问来源于stack exchange,提问作者johannes
相关产品推荐
相关产品推荐

