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

如何实现程序单步追踪?EXCEPTION_SINGLE_STEP触发方法问询

主要问题

我正在尝试编写自定义程序追踪器,但找不到程序自追踪的相关资料,MSDN里也没有关于EXCEPTION_SINGLE_STEP及其调用方式的说明。我知道需要触发EXCEPTION_SINGLE_STEP,但不知道具体操作方法。现在的情况是,首次捕获EXCEPTION_BREAKPOINT异常后,程序会直接运行到结束;如果只是在每条指令前后添加并移除断点,只会持续生成EXCEPTION_BREAKPOINT,而不是EXCEPTION_SINGLE_STEP。

我的代码
BOOL TraceProcess(PEInformation& PEInformation)
{
    DEBUG_EVENT debugEvent; Regs Regs;

    bool IsRunning = true;     
    CONTEXT Context{}; Context.ContextFlags = CONTEXT_ALL;
    HANDLE hThread;
    while (IsRunning)
    {
        if (!WaitForDebugEvent(&debugEvent, INFINITE))
        {
            // Error handling
            DebugActiveProcessStop(PEInformation.processInfo.dwProcessId);
            return FALSE;
        } 

        // Process the debug event based on its type
        switch (debugEvent.dwDebugEventCode)
        {
        case EXCEPTION_DEBUG_EVENT:
            switch (debugEvent.u.Exception.ExceptionRecord.ExceptionCode)
            {
            case EXCEPTION_BREAKPOINT:
                hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, debugEvent.dwThreadId);
                if (!GetThreadContext(hThread, &Context))
                {
                    std::cerr << "GetThreadContext failed: " << GetLastError() << std::endl;
                    break;
                }

                std::cout << "rip: " << std::hex << Context.Rip << std::endl;
                break;
            case EXCEPTION_SINGLE_STEP:
                hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, debugEvent.dwThreadId);
                if (!GetThreadContext(hThread, &Context))
                {
                    std::cerr << "GetThreadContext failed: " << GetLastError() << std::endl;
                    break;
                }

                std::cout << "rip: " << std::hex << Context.Rip << std::endl;
                break;
            }
            break;

        case CREATE_THREAD_DEBUG_EVENT:
            // Handle newly created threads
            // Process debugEvent.u.CreateThread for detailed information
            break;

        case CREATE_PROCESS_DEBUG_EVENT:
            // Handle newly created processes (main thread)
            // Process debugEvent.u.CreateProcessInfo for detailed information
            break;

        case EXIT_THREAD_DEBUG_EVENT:
            // Handle thread exit
            // Process debugEvent.u.ExitThread for detailed information
            break;

        case EXIT_PROCESS_DEBUG_EVENT:
            // Handle process exit
            // Process debugEvent.u.ExitProcess for detailed information
            DebugActiveProcessStop(PEInformation.processInfo.dwProcessId);
            return TRUE;

        case LOAD_DLL_DEBUG_EVENT:
            // Handle DLL loading
            // Process debugEvent.u.LoadDll for detailed information
            break;

        case UNLOAD_DLL_DEBUG_EVENT:
            // Handle DLL unloading
            // Process debugEvent.u.UnloadDll for detailed information
            break;

        case OUTPUT_DEBUG_STRING_EVENT:
            // Handle output of debug strings
            // Process debugEvent.u.DebugString for detailed information
            break;
            // Handle other debug events as needed

        }

        // Continue execution of the traced process
        ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, DBG_CONTINUE);
    }

    return true;
}
解决方案

要触发EXCEPTION_SINGLE_STEP,核心是利用CPU的陷阱标志(Trap Flag)——x86/x64架构中,该标志对应CONTEXT结构体里RFlags(64位)或EFlags(32位)的第8位(掩码0x100)。当此标志置位时,CPU执行完下一条指令后会自动触发单步异常;异常触发后CPU会自动清除该标志,因此需要每次单步后重新置位以持续追踪。

关键代码修改

  1. 处理EXCEPTION_BREAKPOINT时启动单步
    在首次断点触发后,设置陷阱标志并写回线程上下文:

    case EXCEPTION_BREAKPOINT:
        hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, debugEvent.dwThreadId);
        if (!GetThreadContext(hThread, &Context))
        {
            std::cerr << "GetThreadContext failed: " << GetLastError() << std::endl;
            break;
        }
    
        std::cout << "rip: " << std::hex << Context.Rip << std::endl;
    
        // 设置陷阱标志,触发下一条指令的单步异常
        Context.RFlags |= 0x100;
        if (!SetThreadContext(hThread, &Context))
        {
            std::cerr << "SetThreadContext failed: " << GetLastError() << std::endl;
        }
        CloseHandle(hThread); // 释放线程句柄,避免资源泄漏
        break;
    
  2. 处理EXCEPTION_SINGLE_STEP时持续单步
    单步异常触发后,重新设置陷阱标志以继续追踪:

    case EXCEPTION_SINGLE_STEP:
        hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, debugEvent.dwThreadId);
        if (!GetThreadContext(hThread, &Context))
        {
            std::cerr << "GetThreadContext failed: " << GetLastError() << std::endl;
            break;
        }
    
        std::cout << "rip: " << std::hex << Context.Rip << std::endl;
    
        // 重新置位陷阱标志,保持单步追踪
        Context.RFlags |= 0x100;
        if (!SetThreadContext(hThread, &Context))
        {
            std::cerr << "SetThreadContext failed: " << GetLastError() << std::endl;
        }
        CloseHandle(hThread);
        break;
    

额外注意事项

  • 所有通过OpenThread获取的句柄必须用CloseHandle释放,防止资源泄漏
  • 程序启动时的CREATE_PROCESS_DEBUG_EVENT会伴随一次EXCEPTION_BREAKPOINT,这是启动单步追踪的最佳时机
  • 不要用频繁增删硬件断点的方式模拟单步,陷阱标志是CPU原生支持的机制,效率更高且更可靠

内容的提问来源于stack exchange,提问作者Leo Galante

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 21:14:53