SEH
终结处理器: 保证程序在执行的过程中,一定会执行 _finally 块的代码
int main()
{
__try
{
printf("__try { ... }\n");
// 推荐使用 __leave 退出代码块,使用跳转语句会产生多余的函数调用
// __leave 对应实际是一条 jmp 语句,执行更加的迅速
__leave;
// 使用跳转指令退出 __try 块,例如 continue break goto return
goto label_exit;
}
__finally
{
// 通常用于执行某一些特定的清理工作,比如关闭句柄或释放内存
printf("__finally { ... }\n");
// 使用 AbnormalTermination 判断是否是正常退出的
if (AbnormalTermination())
printf("异常退出代码块");
else
printf("正常退出代码块");
}
label_exit:
return 0;
}
SEH
SEH
的两种处理实现方式是不能同时存在的,但是可以嵌套SEH
的处理函数被保存在了栈中,所以不同的线程拥有各自的处理函数异常处理程序SEH
:可以用于捕获产生的异常,并且对它执行相应的处理函数
? __try
:是需要被保护的(可能产生异常)的代码
? __except
:存放过滤表达式和异常处理块
保存异常信息和线程环境的异常结构体
typedef struct _EXCEPTION_POINTERS {
PEXECPTION_RECORD ExceptionRecord; //保存了[异常类型]和[产生异常的指令所在的位置]
PCONTEXT ContextRecord; //保存的是异常发生时的寄存器环境,通过修改可以恢复异常
} EXCEPTION_POINTERS, * PEXCEPTION_POINTERS;
过滤函数:用于根据不同的情况,返回不同类型的值
DWORD FilterHandler(DWORD ExceptionCode, PEXCEPTION_POINTERS ExceptionInfo)
{
// 假设产生的是一个整数除零异常,尝试对异常进行处理,并返回继续执行
if (ExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO)
{
ExceptionInfo->ContextRecord->Ecx = 1;
return EXCEPTION_CONTINUE_EXECUTION;
}
// 否则其它的异常不做处理,向上传递
return EXCEPTION_CONTINUE_SEARCH;
}
__except()过滤表达式的内容可以是任意形式的,但是它的值必须是下面三个之一,通常是函数调用
异常过滤函数通常要用到两个函数调用
GetExceptionCode()
: 获取产生的异常的的类型,只能在过滤表达式和异常处理块中使用GetExceptionInformation()
: 获取异常的信息和异常产生时的线程环境,只能在==过滤表达式中==使用int main()
{
__try
{
printf("__try{ ... }\n");
//可能会产生异常的指令,只有产生了异常才会执行 __except
__asm mov eax, 100
__asm xor edx, edx
__asm xor ecx, ecx
__asm idiv ecx
printf("异常已经被处理了!\n");
//用来触发内存访问异常
//*(DWORD*)0 = 0;
}
__except (FilterHandler(GetExceptionCode(), GetExceptionInformation()))
{
//只有在异常类型为EXCEPTION_EXCUTE_HANDLER才会执行
printf("__except (EXCEPTION_EXECUTE_HANDLER) { ... }");
}
return 0;
UEH
)UEH
): 是应用程序的最后一道防线,当所有的 SEH
都没有能够处理异常,就会执行它
UEH
通常被用于执行内存转储操作,将收集的错误信息(异常类型、线程上下文和内存)提交到服务器UEH
在64位操作系统下的调试器内是==永远不会执行==的,需要单独的运行。自定义的顶层异常处理函数,即使没有自定义,也会有一个默认的处理函数,且只有一个,它的返回值类型和SEH
是相同的,但是缺少了 EXCEPTION_EXECUTE_HANDLER
LONG WINAPI TopLevelExceptionHandler(EXCEPTION_POINTERS* ExceptionInfo)
{
printf("TopLevelExceptionHandler(): %08X\n", ExceptionInfo->ExceptionRecord->ExceptionCode);
// 假设产生的是一个整数除零异常,尝试对异常进行处理,并返回继续执行
if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO)
{
ExceptionInfo->ContextRecord->Ecx = 1;
return EXCEPTION_CONTINUE_EXECUTION; // -1
}
// 否则其它的异常不做处理,向上传递
return EXCEPTION_CONTINUE_SEARCH; // 0
}
int main()
{
// 顶层异常处理的设置依赖于一个函数
SetUnhandledExceptionFilter(TopLevelExceptionHandler);
__try
{
// 产生除零异常
__asm mov eax, 100
__asm xor edx, edx
__asm xor ecx, ecx
__asm idiv ecx
}
__except (EXCEPTION_CONTINUE_SEARCH)
{
// 这里永远不会执行,因为不是 EXCEPTION_EXECUTE_HANDLER(1)
printf("__except (EXCEPTION_CONTINUE_SEARCH)\n");
}
printf("异常处理成功!");
system("pause");
return 0;
}
VEH
)SEH
之前被执行,保存在一个全局的链表中,整个进程都可以访问到。VEH
异常处理函数,它的执行位于 SEH
之前,如果 VEH
没有处理成功,才会调用 SEH
LONG WINAPI VectoredExceptionHandler(EXCEPTION_POINTERS* ExceptionInfo)
{
printf("VectoredExceptionHandler(): %08X\n", ExceptionInfo->ExceptionRecord->ExceptionCode);
// 假设产生的是一个整数除零异常,尝试对异常进行处理,并返回继续执行
if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO)
{
ExceptionInfo->ContextRecord->Ecx = 1;
return EXCEPTION_CONTINUE_EXECUTION; // 0
}
// 否则其它的异常不做处理,向上传递
return EXCEPTION_CONTINUE_SEARCH; // 1
}
int main()
{
// 设置一个向量化异常处理函数(VEH),参数一表示添加到异常处理函数链表的位置
AddVectoredExceptionHandler(TRUE, VectoredExceptionHandler);
__try
{
// 产生除零异常
__asm mov eax, 100
__asm xor edx, edx
__asm xor ecx, ecx
__asm idiv ecx
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
// 这里永远不会执行,因为不是 EXCEPTION_EXECUTE_HANDLER(1)
printf("__except (EXCEPTION_EXECUTE_HANDLER)\n");
}
printf("异常处理成功!");
system("pause");
return 0;
}
VCH
向量化异常处理程序(VCH
):用户层支持的一种机制,在最后被执行
VEH
在同一个表中,只是标志位不同VCH
只会在异常被处理的情况下,最后被执行VEH
-> SEH
-> UEH
-> VCH
自定义 UEH
函数, 在 SEH
之后执行
LONG WINAPI TopLevelExceptionHandler(EXCEPTION_POINTERS* ExceptionInfo)
{
printf("TopLevelExceptionHandler(): %08X\n", ExceptionInfo->ExceptionRecord->ExceptionCode);
// 假设产生的是一个整数除零异常,尝试对异常进行处理,并返回继续执行
if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO)
{
ExceptionInfo->ContextRecord->Ecx = 1;
return EXCEPTION_CONTINUE_EXECUTION;
}
// 否则其它的异常不做处理,向上传递
return EXCEPTION_CONTINUE_SEARCH;
}
自定义的 VEH
异常处理函数,他的执行位于 SEH
之前,如果 VEH
没有处理成功,才会调用 SEH
LONG WINAPI VectoredExceptionHandler(EXCEPTION_POINTERS* ExceptionInfo)
{
printf("VectoredExceptionHandler(): %08X\n", ExceptionInfo->ExceptionRecord->ExceptionCode);
// 否则其它的异常不做处理,向上传递
return EXCEPTION_CONTINUE_SEARCH;
}
自定义的 VCH
异常处理函数,只有在异常处理成功的情况下,最后才会被调用
LONG WINAPI VectoredContinueHandler(EXCEPTION_POINTERS* ExceptionInfo)
{
printf("VectoredContinueHandler(): %08X\n", ExceptionInfo->ExceptionRecord->ExceptionCode);
return EXCEPTION_CONTINUE_SEARCH;
}
过滤函数:用于根据不同的情况,返回不同的类型的值
DWORD FilterHandler(DWORD ExceptionCode, PEXCEPTION_POINTERS ExceptionInfo)
{
printf("FilterHandler(): %08X\n", ExceptionInfo->ExceptionRecord->ExceptionCode);
// 否则其它的异常不做处理,向上传递
return EXCEPTION_CONTINUE_SEARCH;
}
int main()
{
// 设置一个向量化异常处理函数(VEH)
AddVectoredExceptionHandler(TRUE, VectoredExceptionHandler);
// 设置一个向量化异常处理函数(VCH)
AddVectoredContinueHandler(TRUE, VectoredContinueHandler);
// UEH 处理函数
SetUnhandledExceptionFilter(TopLevelExceptionHandler);
__try
{
// 产生除零异常
__asm mov eax, 100
__asm xor edx, edx
__asm xor ecx, ecx
__asm idiv ecx
}
__except (FilterHandler(GetExceptionCode(), GetExceptionInformation()))
{
// 这里永远不会执行,因为不是 EXCEPTION_EXECUTE_HANDLER(1)
printf("__except (EXCEPTION_EXECUTE_HANDLER)\n");
}
printf("异常处理成功!");
system("pause");
return 0;
}
SEH
原理剖析SEH
保存在TEB
结构体偏移为0的地方,是一个链表,SEH
链表可以通过FS : [0]
这样一个地址找到
TEB
可以通过FS:[0X18]
这样一个地址找到。
try except
的原理就是在 SEH
链表中添加一个新的节点
// 自定义的SEH 函数
EXCEPTION_DISPOSITION NTAPI ExceptionHandler(
struct _EXCEPTION_RECORD* ExceptionRecord,
PVOID EstablisherFrame,
struct _CONTEXT* ContextRecord,
PVOID DispatcherContext)
{
return ExceptionContinueSearch;
}
//遍历当前线程中的所有 SEH函数
void GetThreadList()
{
//1. 获取当前 SEH 链表的头节点
PEXCEPTION_REGISTRATION_RECORD ExceptionList = nullptr;
__asm
{
push FS : [0]
POP ExceptionList
}
//遍历 SEH 中的所有函数
while (ExceptionList != (PEXCEPTION_REGISTRATION_RECORD)-1)
{
//输出当前层,对应的处理函数
printf("0x%08X\n", ExceptionList->Handler);
//将指针指向下一个节点
ExceptionList = ExceptionList->Next;
}
printf("\n");
}
int main()
{
// 0 保存 SEH 头节点,主要用于恢复
PEXCEPTION_REGISTRATION_RECORD ExceptionList = nullptr;
__asm
{
push FS : [0];
pop ExceptionList;
}
// 1 添加自定义 SEH 函数之前的 SEH 链
GetThreadList();
// 2 添加自定义 SEH 函数之前的 SEH 链
__asm
{
push ExceptionHandler
push fs : [0]
mov fs : [0], esp
}
// 3 添加自定义 SEH 函数之后的 SEH 链
GetThreadList();
// 4 恢复旧的 SEH 头节点
__asm
{
add esp, 0x08
mov eax, ExceptionList
mov fs : [0], eax
}
// 5 应该和以前的节点是相同的
GetThreadList();
return 0;
}
int3
异常的的函数为KitTrap03
TRAP_FRAME
陷阱帧结构,陷阱帧是指一个结构体,用来保存系统调用、中断、异常发生时的寄存器现场,方便以后回到用户空间/回到中断处时,恢复那些寄存器的值,继续执行。KitTrap03
实际上调用了CommonDispatchException
IDT:
中断描述表(Interrupt Descriptor Table
)
CPU
特权级别 R0
~R3
共三个级别,操作系统将执行状态分成了内核态和用户态,内核态时的CPU特别级置为R0
,用户台的特别级置为R3
原文:https://www.cnblogs.com/TJTO/p/11373984.html