考虑一下我故意造成访问冲突的代码:
#include <Windows.h>
int main(int argc, char** argv)
{
__try
{
*((unsigned char*)0) = 0;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
__asm int 3;
}
return 0;
}在无效访问的指令下,EFLAGS为0x282 (在执行它之前)。当我单步执行时,它会变成0x10382。然后,我再次单步将异常传递给应用程序。异常处理程序处理异常并返回到正常的执行流程。但是,这一次EFLAGS已更改为0x244。其他一些寄存器也已更改。
如果可能在任何指令上发生异常,当异常处理后,EFLAGS和可能的其他寄存器可能包含与异常发生之前不同的值时,编译器如何确定其对EFLAGS和其他寄存器的使用?这将破坏此后的每个分支决策。
发布于 2017-03-05 07:24:24
你的问题是基于一个错误的前提,即
异常处理程序处理异常并返回到正常的执行流程。
您可以运行异常处理程序,也可以返回到正常的执行流程,但不能同时运行两者。在您发布的代码中,控制流从__except块下降到return语句,它永远不会重新进入__try块。
编译器(无法知道异常是否会被触发)将始终生成在任何一种情况下都能工作的代码。
如果您希望在调试器中实际看到这种情况发生,则需要一些生成异常的代码,该异常可以从以下位置继续:
#include <Windows.h>
#include <stdio.h>
static char * ptr;
int filter(void)
{
__asm int 3; // breakpoint A
if (VirtualAlloc(ptr, 1, MEM_COMMIT, PAGE_EXECUTE_READWRITE) == NULL)
{
return EXCEPTION_EXECUTE_HANDLER;
}
else
{
return EXCEPTION_CONTINUE_EXECUTION;
}
}
int main(int argc, char** argv)
{
ptr = (char *)VirtualAlloc(NULL, 1, MEM_RESERVE, PAGE_EXECUTE_READWRITE);
__try
{
__asm int 3; // breakpoint B
*ptr = 0;
__asm int 3; // breakpoint C
printf("Hi\n");
}
__except (filter())
{
printf("Oops\n");
}
return 0;
}此程序将首先命中断点B。您可能想要截图,或者记下寄存器的值。

由于内存块是保留的,但未提交,因此尝试写入它将导致抛出异常;这将调用filter()并命中断点A。各种标志和寄存器已经更改,并且随着filter()的运行,更多的标志和寄存器将更改,但是一旦控制流返回到发生异常的位置,即命中断点C时,您将发现所有原始寄存器值都已恢复:

这是可能的,因为内核存储了发生异常的程序上下文。
另请参阅GetExceptionInformation,,它允许过滤器表达式确定有关异常的信息,包括在引发异常时保存的寄存器值。
https://stackoverflow.com/questions/42591286
复制相似问题