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

Detours Hook CreateFileW获取调用栈报126/184错及重复输出问题

问题描述

通过Detours库Hook Win32 API CreateFileW,调用CaptureStackBackTrace捕获调用栈,使用SymFromAddr解析栈地址对应符号时,接口返回错误码126、184,符号解析失败。代码中仅显式调用一次ShowTraceStack函数,运行时却输出多份栈追踪信息。

附原实现代码:

#include <windows.h>
#include <stdio.h>
#include "detours.h"
#include <fstream>
#include <Shlwapi.h>
#pragma comment(lib, "shlwapi.lib")  //Windows API   PathFileExists
#include <io.h>   
#pragma comment(lib, "detours.lib")
#include <DbgHelp.h>                    //SymInitialize
#pragma comment(lib,"dbghelp.lib")
#define STACK_INFO_LEN  20000

struct stackInfo {
    PDWORD hashValue; // hash value to identify same stack
    char* szBriefInfo; // callstack info
};
stackInfo ShowTraceStack(char* szBriefInfo)
{
    static const int MAX_STACK_FRAMES = 200;
    void* pStack[MAX_STACK_FRAMES];
    static char szStackInfo[STACK_INFO_LEN * MAX_STACK_FRAMES];
    static char szFrameInfo[STACK_INFO_LEN];

    HANDLE process = GetCurrentProcess(); // The handle used must be unique to avoid sharing a session with another component,
    SymInitialize(process, NULL, TRUE);
    PDWORD hashValue = (PDWORD)malloc(sizeof(DWORD)); // allow memory for hashVavlue, it will be rewrited in function CaptureStackBackTrace
    WORD frames = CaptureStackBackTrace(0, MAX_STACK_FRAMES, pStack, hashValue);
    //printf("hash value is: %ud \n", &hashValue);
    if (szBriefInfo == NULL) {
        strcpy_s(szStackInfo, "stack traceback:\n");
    }
    else {
        strcpy_s(szStackInfo, szBriefInfo);
    }

    for (WORD i = 0; i < frames; ++i) {
        DWORD64 address = (DWORD64)(pStack[i]);

        DWORD64 displacementSym = 0;
        char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
        PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;
        pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
        pSymbol->MaxNameLen = MAX_SYM_NAME;

        DWORD displacementLine = 0;
        IMAGEHLP_LINE64 line;
        line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);

        if (SymFromAddr(process, address, &displacementSym, pSymbol) &&
            SymGetLineFromAddr64(process, address, &displacementLine, &line))
        {
            _snprintf_s(szFrameInfo, sizeof(szFrameInfo), "\t%s() at %s:%d(0x%x)\n",
                pSymbol->Name, line.FileName, line.LineNumber, pSymbol->Address);
        }
        else
        {
            _snprintf_s(szFrameInfo, sizeof(szFrameInfo), "\terror: %d\n", GetLastError());
        }
        strcat_s(szStackInfo, szFrameInfo);
    }
    stackInfo traceStackInfo;
    traceStackInfo.hashValue = hashValue;
    traceStackInfo.szBriefInfo = szStackInfo;
    printf("%s", szStackInfo); 

    return traceStackInfo;
}
HANDLE(*oldCreateFile)(LPCWSTR,
    DWORD,
    DWORD,
    LPSECURITY_ATTRIBUTES,
    DWORD,
    DWORD,
    HANDLE) = CreateFileW;

HANDLE WINAPI newCreateFile(
    _In_ LPCWSTR lpFileName,
    _In_ DWORD dwDesiredAccess,
    _In_ DWORD dwShareMode,
    _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
    _In_ DWORD dwCreationDisposition,
    _In_ DWORD dwFlagsAndAttributes,
    _In_opt_ HANDLE hTemplateFile
) {
    ShowTraceStack((char*)"trace information.");
    return oldCreateFile(
        L".\\newFiles.txt", // L".\\NewFile.txt",     // Filename
        //lpFileName,
        dwDesiredAccess,          // Desired access
        dwShareMode,        // Share mode
        lpSecurityAttributes,                   // Security attributes
        dwCreationDisposition,             // Creates a new file, only if it doesn't already exist
        dwFlagsAndAttributes,  // Flags and attributes
        NULL);
}

void hook() {
    DetourRestoreAfterWith();
    DetourTransactionBegin();
    DetourUpdateThread(GetCurrentThread());
    DetourAttach(&(PVOID&)oldCreateFile, newCreateFile);
    DetourTransactionCommit();
}

void unhook()
{
    DetourTransactionBegin();
    DetourUpdateThread(GetCurrentThread());
    DetourDetach(&(PVOID&)oldCreateFile, newCreateFile);
    DetourTransactionCommit();
}

void myProcess() {
    HANDLE hFile = CreateFile(TEXT(".\\CreateFileDemo.txt"),    
        GENERIC_WRITE | GENERIC_READ,          
        0,                      
        NULL,                   
        CREATE_ALWAYS,          
        FILE_ATTRIBUTE_NORMAL,        
        NULL);                
    if (hFile == INVALID_HANDLE_VALUE)
    {
        OutputDebugString(TEXT("CreateFile fail!\r\n"));
    }

    // write to file 
    const int BUFSIZE = 4096;
    char chBuffer[BUFSIZE];
    memcpy(chBuffer, "Test", 4);
    DWORD dwWritenSize = 0;
    BOOL bRet = WriteFile(hFile, chBuffer, 4, &dwWritenSize, NULL);
    ShowTraceStack((char*)"trace information.");  

    if (bRet)
    {
        OutputDebugString(TEXT("WriteFile success!\r\n"));
    }

}

int main(){
  
    hook();

    myProcess();

    unhook();
}

附运行结果截图:
运行结果截图

故障原因
  • 符号解析错误原因
    • 错误码126对应ERROR_MOD_NOT_FOUND:SymInitialize每次调用ShowTraceStack都重复执行,破坏了DbgHelp的符号会话,且未提前配置符号加载选项、未正确加载对应模块的符号文件,导致找不到模块对应符号。
    • 错误码184对应ERROR_INVALID_ADDRESS:Hook递归触发时栈帧混乱,传入SymFromAddr的地址包含无效地址、未加载模块的地址,无法解析。
  • 多份栈输出原因:Hook逻辑存在递归调用问题。ShowTraceStack内部的printf输出、DbgHelp加载符号文件、C运行时IO操作,以及Hook函数中硬编码打开newFiles.txt的逻辑,都会隐式调用CreateFileW,再次触发Hook进入ShowTraceStack,导致递归打印多份栈信息,甚至栈溢出。
修复方案
  1. 调整DbgHelp初始化逻辑:仅在程序启动、Hook安装前初始化一次DbgHelp,提前配置符号加载选项,不要在每次抓栈时重复初始化。
  2. 增加线程级递归防护:进入Hook处理逻辑前先打标记,标记生效期间所有CreateFileW调用直接走原函数,不触发栈打印逻辑,避免递归。
  3. 修正Hook逻辑:不要在Hook函数中硬编码修改打开的文件路径,避免无意义的文件触发额外调用;修正CaptureStackBackTrace的哈希参数用法,不需要手动malloc内存,直接使用局部变量存储哈希值即可,避免内存泄漏。

关键修正代码:

// 全局添加线程局部递归防护标记
thread_local bool g_inHookProc = false;

int main(){
    // 初始化DbgHelp,仅执行一次
    SymSetOptions(SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_DEFERRED_LOADS);
    SymInitialize(GetCurrentProcess(), NULL, TRUE);
    // 如需解析系统API符号,可添加符号路径配置
    // SymSetSearchPath(GetCurrentProcess(), L"SRV*C:\\Symbols*https://msdl.microsoft.com/download/symbols;");

    hook();
    myProcess();
    unhook();

    // 程序退出前清理DbgHelp
    SymCleanup(GetCurrentProcess());
    return 0;
}

// 修改Hook函数,增加递归防护
HANDLE WINAPI newCreateFile(
    _In_ LPCWSTR lpFileName,
    _In_ DWORD dwDesiredAccess,
    _In_ DWORD dwShareMode,
    _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
    _In_ DWORD dwCreationDisposition,
    _In_ DWORD dwFlagsAndAttributes,
    _In_opt_ HANDLE hTemplateFile
) {
    // 处于Hook处理流程中时,直接调用原函数
    if (g_inHookProc) {
        return oldCreateFile(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes,
            dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
    }
    g_inHookProc = true;
    ShowTraceStack((char*)"CreateFileW call trace:");
    // 不要硬编码修改文件名,使用原调用的参数
    HANDLE hRet = oldCreateFile(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes,
        dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
    g_inHookProc = false;
    return hRet;
}

// 修改ShowTraceStack,删除内部重复的SymInitialize调用,修正hash参数用法
stackInfo ShowTraceStack(char* szBriefInfo)
{
    static const int MAX_STACK_FRAMES = 200;
    void* pStack[MAX_STACK_FRAMES];
    static char szStackInfo[STACK_INFO_LEN * MAX_STACK_FRAMES];
    static char szFrameInfo[STACK_INFO_LEN];

    HANDLE process = GetCurrentProcess();
    DWORD hashValue = 0; // 直接用局部变量存哈希,不需要malloc
    WORD frames = CaptureStackBackTrace(0, MAX_STACK_FRAMES, pStack, &hashValue);
    // 剩余逻辑保持不变,删除原函数中SymInitialize(process, NULL, TRUE);这行
    // ...
}

内容的提问来源于stack exchange,提问作者GuangJun Liu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 07:42:27