我一直在编写代码以尝试监视堆栈跟踪,并产生了尝试使用调试寄存器和向量化异常处理(针对 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/