You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 07:05:14