Catching Stacktraces with Debug Register Breakpoints and Vectored Exception Handling

2

I've been working on code to attempt to monitor for stack traces and had the idea to try to use debug registers and vectored exception handling (for x86). I've made an example program that will output the address of whatever accesses the current return address. This works if a stack trace occurs within the function called. If it doesn't (comment out the code which reads the return address) the exception triggers on the return instruction. From here my exception handler isn't called and the program throws another exception and then terminates with a stackhash error. Is there any way to make this catch the stack trace if it occurs but also not crash on the return if a stack trace does not occur?

Here's the code of my test program written for 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;
}
c
assembly
x86
asked on Stack Overflow Feb 5, 2017 by dottiff

1 Answer

0

you need code like this:

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);
    }
}

this will be worked for both x86 and x64. you not need use inline asm. main your error - forget ContextRecord->ContextFlags |= CONTEXT_DEBUG_REGISTERS; line

answered on Stack Overflow Feb 5, 2017 by RbMm

User contributions licensed under CC BY-SA 3.0