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

Hook CreateFile时SymInitialize()调用CreateFile()触发死循环的解决方法

Hook CreateFile记录调用栈触发死循环问题说明

问题背景

开发中计划对CreateFile()函数进行Hook并重写逻辑,同时在自定义的CreateFile()实现中记录调用栈信息。实际运行时调用SymInitialize()初始化符号处理句柄,程序直接陷入无限死循环。调试确认根因为SymInitialize()内部会调用CreateFile(),递归触发自定义Hook函数的栈采集逻辑,最终栈溢出死循环。

问题解答

1. SymInitialize()内部调用CreateFile()的具体原因

SymInitialize()是DbgHelp.dll提供的符号解析引擎初始化接口:

  • 当接口第三个参数fInvadeProcess传入TRUE时,会自动遍历当前进程已加载的所有PE模块(exe、dll、sys等),逐一读取磁盘上的模块文件、搜索匹配的PDB符号文件,这些文件访问操作都依赖CreateFile()完成。
  • 即使fInvadeProcess传入FALSE,后续调用SymFromAddr()、SymGetLineFromAddr64()等符号解析接口时,也会按需调用CreateFile()加载对应模块的符号文件,本质都是符号加载流程的正常文件访问需求。

2. 递归死循环规避方案

核心是阻断递归调用路径,按可靠性从高到低排序:

  • 加线程级重入锁:使用线程局部存储(TLS)定义布尔标记,进入自定义Hook函数时先检查标记,若当前线程已处于Hook处理流程中,直接调用原始CreateFile逻辑,跳过栈采集步骤;若未处于处理流程,先将标记置为有效,执行完所有逻辑(包括栈采集)后再将标记恢复为无效。该方案可以覆盖所有DbgHelp接口触发的CreateFile调用,是最通用的解决方式。

重入标记简单实现参考:

thread_local bool g_isInHookLogic = false;

HANDLE WINAPI newCreateFile(/* 函数参数保持和原定义一致 */) {
    if (g_isInHookLogic) {
        return oldCreateFile(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
    }
    g_isInHookLogic = true;
    // 执行栈采集、自定义业务逻辑
    ShowTraceStack((char*)"trace information.\n");
    HANDLE res = oldCreateFile(L".\\newFiles.txt", dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, NULL);
    g_isInHookLogic = false;
    return res;
}
  • 提前初始化符号引擎:SymInitialize()属于一次性初始化接口,一个进程只需要调用一次。可以在安装Hook之前就完成SymInitialize()调用,提前加载完所有需要的模块符号,避免Hook生效后初始化流程触发递归。注意不要在每次栈采集时重复调用该接口,既会引发递归,也违反DbgHelp的使用规范。
  • 符号操作临时卸载Hook:执行栈采集和符号解析前临时卸载Hook,操作完成后重新挂载,该方案存在线程安全漏洞,多线程场景下不推荐使用。

3. 无递归风险的调用栈记录替代方案

可以从架构上拆分栈采集和符号解析流程,从根源避免DbgHelp触发文件调用:

  • 分离原始栈采集和符号解析流程:Hook逻辑中只调用CaptureStackBackTrace或RtlCaptureStackBackTrace采集原始栈帧地址、计算栈哈希,这两个接口完全在内存中完成栈回溯,不会触发任何文件操作。采集到的原始栈数据存入线程安全的队列,由独立的、不挂载Hook的工作线程异步完成后续符号解析、日志存储操作。
  • 使用ETW内核追踪:通过系统提供的事件追踪机制,在内核层捕获文件创建事件和对应调用栈,完全不需要用户态Hook,不存在重入问题,性能远高于用户态Inline Hook,适合生产环境使用。

问题复现代码

#include <windows.h>
#include <stdio.h>
#include "detours.h"
#include <fstream>
#include <io.h>   

#pragma comment(lib, "detours.lib")
#include <DbgHelp.h>                    //SymInitialize
#pragma comment(lib,"dbghelp.lib")
#define STACK_INFO_LEN  200

struct stackInfo {
    PDWORD hashValue; // hash value to identify same stack
    char* szBriefInfo; // callstack info
};

stackInfo ShowTraceStack(char* szBriefInfo)
{
    static const int MAX_STACK_FRAMES = 12;
    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);
    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;
    return traceStackInfo;
}

HANDLE (*__stdcall 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.\n");
    return oldCreateFile(
        L".\\newFiles.txt",
        dwDesiredAccess,
        dwShareMode,
        lpSecurityAttributes,
        dwCreationDisposition,
        dwFlagsAndAttributes,
        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"));
    }
    const int BUFSIZE = 4096;
    char chBuffer[BUFSIZE];
    memcpy(chBuffer, "Test", 4);
    DWORD dwWritenSize = 0;
    BOOL bRet = WriteFile(hFile, chBuffer, 4, &dwWritenSize, NULL);
    if (bRet) {
        OutputDebugString(TEXT("WriteFile success!\r\n"));
    }
}

int main(){
    hook();
    myProcess();
    unhook();
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.31 12:48:15