使用调试寄存器断点和向量化异常处理捕获堆栈跟踪

标签 c assembly x86

我一直在编写代码以尝试监视堆栈跟踪,并产生了尝试使用调试寄存器和向量化异常处理(针对 x86)的想法。我制作了一个示例程序,它将输出任何访问当前返回地址的地址。如果在调用的函数中出现堆栈跟踪,则此方法有效。如果不是(注释掉读取返回地址的代码),则异常会在返回指令上触发。从这里我的异常处理程序没有被调用,程序抛出另一个异常,然后以 stackhash 错误终止。有什么方法可以使它在发生时捕获堆栈跟踪,但如果堆栈跟踪不发生也不会在返回时崩溃?

这是我为 Visual Studio 2012 (C, x86) 编写的测试程序的代码

#include <Windows.h>
#include <stdio.h>

DWORD WINAPI myFunction(void);
void myFunction2(void);
DWORD WINAPI ExceptionHandler(EXCEPTION_POINTERS *pExceptionInfo);

int main()
{
    HANDLE thread = GetCurrentThread();
    static CONTEXT context;

    AddVectoredExceptionHandler(1, (PVECTORED_EXCEPTION_HANDLER)ExceptionHandler); 
    context.ContextFlags = CONTEXT_DEBUG_REGISTERS;
    GetThreadContext(thread, &context);
    context.Dr6 = 0;

    context.Dr7 = 0;
    context.Dr7 |= (3 << 16); //set bits 16-17 to 1 - break read/write
    context.Dr7 |= (3 << 18); //set bits 18-19 to 1 - size = 4
    context.Dr7 |= 0x101; //enable local hardware breakpoint on dr0
    context.Dr7 |= 1 << 13;

    _asm {
        push offset label //push return address
        lea eax, [esp]
        mov context.Dr0, esp
    }
    SetThreadContext(thread, &context);
    __asm
    {
        jmp myFunction // "call" myFunction
label:
    }

    printf("Return from my function\n");
    getchar(); 


    return 0;
}


// try windows api stack trace
DWORD WINAPI myFunction(void)
{
    PVOID out[10];
    CaptureStackBackTrace(0, 10, out, 0);
    return 0;
}

// simple pull return address from esp
void __declspec(naked) myFunction2(void)
{
    __asm{ 
        mov eax, [esp]
        ret
    }
}


DWORD WINAPI ExceptionHandler(EXCEPTION_POINTERS *pExceptionInfo)
{
    //Handle hwbp

    if(pExceptionInfo->ExceptionRecord->ExceptionCode==EXCEPTION_SINGLE_STEP && //hardware breakpoints are SINGLE_STEP
        (pExceptionInfo->ContextRecord->Dr6 & 1)) //check to see that instruction occured on Dr0
    {
        pExceptionInfo->ContextRecord->Dr7 &= 0xfffffffe; // disable Dr0
        printf("Hardware breakpoint triggered at %08Xh\n", pExceptionInfo->ExceptionRecord->ExceptionAddress);
        return EXCEPTION_CONTINUE_EXECUTION;
    }

    printf("Other exception\n");
    return EXCEPTION_CONTINUE_SEARCH;
}

最佳答案

你需要这样的代码:

LONG WINAPI ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo)
{
    PEXCEPTION_RECORD ExceptionRecord = ExceptionInfo->ExceptionRecord;

    if (ExceptionRecord->ExceptionCode != STATUS_SINGLE_STEP) return EXCEPTION_CONTINUE_SEARCH;

    CONTEXT* ContextRecord = ExceptionInfo->ContextRecord;

    if (!_bittest((PLONG)&ContextRecord->Dr6, 3))
    {
        return EXCEPTION_CONTINUE_SEARCH;
    }

    ContextRecord->EFlags |= RESUME_FLAG;
    ContextRecord->Dr7 = 0;
    ContextRecord->Dr3 = 0;
    ContextRecord->ContextFlags |= CONTEXT_DEBUG_REGISTERS;

    return EXCEPTION_CONTINUE_EXECUTION;
}

void test()
{
    if (AddVectoredExceptionHandler(TRUE, ExceptionHandler))
    {
        CONTEXT ctx;
        ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
        ctx.Dr7 = 0xF0000040;
        ctx.Dr6 = 0;
        ctx.Dr3 = (ULONG_PTR)_AddressOfReturnAddress();

        SetThreadContext(NtCurrentThread(), &ctx);
    }
}

这将适用于 x86 和 x64。你不需要使用内联汇编。主要是你的错误 - 忘记 ContextRecord->ContextFlags |= CONTEXT_DEBUG_REGISTERS;

关于使用调试寄存器断点和向量化异常处理捕获堆栈跟踪,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42053067/

相关文章:

CMAKE/MAKEFILE :/usr/bin/x86_64-linux-gnu-ld : Cannot find -lfftw3f

c++ - 为什么 g++ 将计算拉入热循环?

assembly - 如何在Assembly中字节数的2位之间交换

c - 为什么 -O1 比 -O2 快

c++ - 如何定义和测试字符串值的宏?

c - 在二维数组中查找 8 个邻居

android - Atom/x86 与 Android 设备上的 ARM CPU 代码执行

architecture - 将ARM指令转换为i386指令

C 字符串搜索获取字符串的一部分

linux - 需要帮助获取未签名的产品 NASM