什么是SEH?
Windows下各种异常处理的优先级
认识SEH链及处理机制
SEH的注册及SEH的删除
异常的种类和常见的异常代码
SEH的异常处理
小结
SEH(
Structured Exception Handling ,
结构化异常处理
)
结构化异常处理(SEH)是Windows操作系统提供的强大异常处理功能。而Visual C++中的__try{}/__finally{}和__try{}/__except{}结构本质上是对Windows提供的SEH的封装
STATUS_ACCESS_VIOLATION(0xC0000005)
STATUS_BREAKPOINT( 0x80000003 )
STATUS_ILLEGAL_INSTRUCTION(0xC000001D)
STATUS_INTEGER_DIVIDE_BY_ZERO(0xC0000094)
STATUS_SINGLE_STEP
什么是SEH?
Windows下各种异常处理的优先级
认识SEH链及处理机制
SEH的注册及SEH的删除
异常的种类和常见的异常代码
SEH的异常处理
小结
上一章简单介绍了TEB PEB在fs寄存器中的位置及常用的结构体成员,接下来我们开始认识SEH:
(一)、
什么是SEH?
SEH(
Structured Exception Handling ,
结构化异常处理
)
结构化异常处理(SEH)是Windows操作系统提供的强大异常处理功能。而Visual C++中的__try{}/__finally{}和__try{}/__except{}结构本质上是对Windows提供的SEH的封装
在这里可以看到我们把EAX的值置为空指针,然后向空指针里写入值,引发 STATUS_ACCESS_VIOLATION(内存访问异常) ,然后在异常处理里面把
EAX的值设置为 变量dwTest的地址,然后返回 EXCEPTION_CONTINUE_EXECUTION 表示异常被处理,从异常处继续执行,这里是MSDN
对于异常处理(
Exception Handling
)返回值的定义:
#define EXCEPTION_EXECUTE_HANDLER 1 //表示异常被处理,从下一条指令开始执行
#define EXCEPTION_CONTINUE_SEARCH 0 //表示异常未被处理,交由下一个SEH
#define EXCEPTION_CONTINUE_EXECUTION -1 //表示异常被处理,从异常处开始执行
对于上面这段定义,很多人给出的注释不同,以上注释是我对他们的实验结果和理解。
#define EXCEPTION_EXECUTE_HANDLER 1 //表示异常被处理,从下一条指令开始执行
#define EXCEPTION_CONTINUE_SEARCH 0 //表示异常未被处理,交由下一个SEH
#define EXCEPTION_CONTINUE_EXECUTION -1 //表示异常被处理,从异常处开始执行
对于上面这段定义,很多人给出的注释不同,以上注释是我对他们的实验结果和理解。
(二)、
Windows下各种异常处理的优先级
PVOID WINAPI AddVectoredExceptionHandler(
_In_ ULONG FirstHandler,
_In_ PVECTORED_EXCEPTION_HANDLER VectoredHandler
);
PVOID WINAPI AddVectoredContinueHandler(
_In_ ULONG FirstHandler,
_In_ PVECTORED_EXCEPTION_HANDLER VectoredHandler
);
向进程里注册一个异常捕获函数,参数FirstHandler
决定插入到链表的位置(非0为头部,
0为底部
)
,异常处理中最后执行
PVOID WINAPI AddVectoredContinueHandler(
_In_ ULONG FirstHandler,
_In_ PVECTORED_EXCEPTION_HANDLER VectoredHandler
);
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilter(
_In_ LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter
);
TopLevelEH 为线程顶级异常处理器,通常可以处理到所有线程消息发生的异常。
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilter(
_In_ LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter
);
TopLevelEH 为线程顶级异常处理器,通常可以处理到所有线程消息发生的异常。
这里我们可以发现以上异常处理回调函数参数大都为 EXCEPTION_POINTERS 结构体,我们查询下它的结构:
typedef struct _EXCEPTION_POINTERS {
PEXCEPTION_RECORD ExceptionRecord;
PCONTEXT ContextRecord;
} EXCEPTION_POINTERS, *PEXCEPTION_POINTERS;
EXCEPTION_RECORD:
typedef struct _EXCEPTION_RECORD {
DWORD ExceptionCode; //异常码,以STATUS_或EXCEPTION_开头,可自定义。(sehdef.inc)
DWORD ExceptionFlags; //异常标志。0可修复;1不可修复;2正在展开,不要试图修复
struct _EXCEPTION_RECORD *ExceptionRecord; //指向嵌套的异常结构,通常是异常中又引发异常
PVOID ExceptionAddress; //异常发生的地址
DWORD NumberParameters; //下面ExceptionInformation所含有的dword数目
ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; //附加消息,如读或写冲突
} EXCEPTION_RECORD;
CONTEXT:
typedef struct _CONTEXT {
//
// The flags values within this flag control the contents of
// a CONTEXT record.
//
// If the context record is used as an input parameter, then
// for each portion of the context record controlled by a flag
// whose value is set, it is assumed that that portion of the
// context record contains valid context. If the context record
// is being used to modify a threads context, then only that
// portion of the threads context will be modified.
//
// If the context record is used as an IN OUT parameter to capture
// the context of a thread, then only those portions of the thread's
// context corresponding to set flags will be returned.
//
// The context record is never used as an OUT only parameter.
//
DWORD ContextFlags;
//
// This section is specified/returned if CONTEXT_DEBUG_REGISTERS is
// set in ContextFlags. Note that CONTEXT_DEBUG_REGISTERS is NOT
// included in CONTEXT_FULL.
//
DWORD Dr0;
DWORD Dr1;
DWORD Dr2;
DWORD Dr3;
DWORD Dr6;
DWORD Dr7;
//
// This section is specified/returned if the
// ContextFlags word contians the flag CONTEXT_FLOATING_POINT.
//
FLOATING_SAVE_AREA FloatSave;
//
// This section is specified/returned if the
// ContextFlags word contians the flag CONTEXT_SEGMENTS.
//
DWORD SegGs;
DWORD SegFs;
DWORD SegEs;
DWORD SegDs;
//
// This section is specified/returned if the
// ContextFlags word contians the flag CONTEXT_INTEGER.
//
DWORD Edi;
DWORD Esi;
DWORD Ebx;
DWORD Edx;
DWORD Ecx;
DWORD Eax;
//
// This section is specified/returned if the
// ContextFlags word contians the flag CONTEXT_CONTROL.
//
DWORD Ebp;
DWORD Eip;
DWORD SegCs; // MUST BE SANITIZED
DWORD EFlags; // MUST BE SANITIZED
DWORD Esp;
DWORD SegSs;
//
// This section is specified/returned if the ContextFlags word
// contains the flag CONTEXT_EXTENDED_REGISTERS.
// The format and contexts are processor specific
//
BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];
} CONTEXT;
CONTEXT结构体大家应该都懂!
typedef struct _EXCEPTION_POINTERS {
PEXCEPTION_RECORD ExceptionRecord;
PCONTEXT ContextRecord;
} EXCEPTION_POINTERS, *PEXCEPTION_POINTERS;
EXCEPTION_RECORD:
typedef struct _EXCEPTION_RECORD {
DWORD ExceptionCode; //异常码,以STATUS_或EXCEPTION_开头,可自定义。(sehdef.inc)
DWORD ExceptionFlags; //异常标志。0可修复;1不可修复;2正在展开,不要试图修复
struct _EXCEPTION_RECORD *ExceptionRecord; //指向嵌套的异常结构,通常是异常中又引发异常
PVOID ExceptionAddress; //异常发生的地址
DWORD NumberParameters; //下面ExceptionInformation所含有的dword数目
ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; //附加消息,如读或写冲突
} EXCEPTION_RECORD;
CONTEXT:
typedef struct _EXCEPTION_RECORD {
DWORD ExceptionCode; //异常码,以STATUS_或EXCEPTION_开头,可自定义。(sehdef.inc)
DWORD ExceptionFlags; //异常标志。0可修复;1不可修复;2正在展开,不要试图修复
struct _EXCEPTION_RECORD *ExceptionRecord; //指向嵌套的异常结构,通常是异常中又引发异常
PVOID ExceptionAddress; //异常发生的地址
DWORD NumberParameters; //下面ExceptionInformation所含有的dword数目
ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; //附加消息,如读或写冲突
} EXCEPTION_RECORD;
CONTEXT:
typedef struct _CONTEXT {
//
// The flags values within this flag control the contents of
// a CONTEXT record.
//
// If the context record is used as an input parameter, then
// for each portion of the context record controlled by a flag
// whose value is set, it is assumed that that portion of the
// context record contains valid context. If the context record
// is being used to modify a threads context, then only that
// portion of the threads context will be modified.
//
// If the context record is used as an IN OUT parameter to capture
// the context of a thread, then only those portions of the thread's
// context corresponding to set flags will be returned.
//
// The context record is never used as an OUT only parameter.
//
DWORD ContextFlags;
//
// This section is specified/returned if CONTEXT_DEBUG_REGISTERS is
// set in ContextFlags. Note that CONTEXT_DEBUG_REGISTERS is NOT
// included in CONTEXT_FULL.
//
DWORD Dr0;
DWORD Dr1;
DWORD Dr2;
DWORD Dr3;
DWORD Dr6;
DWORD Dr7;
//
// This section is specified/returned if the
// ContextFlags word contians the flag CONTEXT_FLOATING_POINT.
//
FLOATING_SAVE_AREA FloatSave;
//
// This section is specified/returned if the
// ContextFlags word contians the flag CONTEXT_SEGMENTS.
//
DWORD SegGs;
DWORD SegFs;
DWORD SegEs;
DWORD SegDs;
//
// This section is specified/returned if the
// ContextFlags word contians the flag CONTEXT_INTEGER.
//
DWORD Edi;
DWORD Esi;
DWORD Ebx;
DWORD Edx;
DWORD Ecx;
DWORD Eax;
//
// This section is specified/returned if the
// ContextFlags word contians the flag CONTEXT_CONTROL.
//
DWORD Ebp;
DWORD Eip;
DWORD SegCs; // MUST BE SANITIZED
DWORD EFlags; // MUST BE SANITIZED
DWORD Esp;
DWORD SegSs;
//
// This section is specified/returned if the ContextFlags word
// contains the flag CONTEXT_EXTENDED_REGISTERS.
// The format and contexts are processor specific
//
BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];
} CONTEXT;
CONTEXT结构体大家应该都懂!
下面我们简单的写一个Demo,试验一下他们的处理顺序:
// SEHTest.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
//
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
//
LONG __stdcall VEHandler(
EXCEPTION_POINTERS *ExceptionInfo
)
{
printf("VEHandler\n");
return EXCEPTION_CONTINUE_SEARCH;
}
LONG __stdcall VCHandler(
EXCEPTION_POINTERS *ExceptionInfo
)
{
printf("VCHandler\n");
ExceptionInfo->ContextRecord->Eip++;
return EXCEPTION_CONTINUE_EXECUTION;
}
//
LONG NTAPI TopLevelExcepFilter(EXCEPTION_POINTERS *pExcepInfo)
{
printf("TopLevelEHandler\n");
return EXCEPTION_CONTINUE_EXECUTION;
}
//
LONG NTAPI SEHander(EXCEPTION_POINTERS *ExceptionInfo){
//异常处理
printf("SEHandler\n");
return EXCEPTION_CONTINUE_SEARCH;
}
int _tmain(int argc, _TCHAR* argv[])
{
AddVectoredExceptionHandler(0,VEHandler);
AddVectoredContinueHandler(0,VCHandler);
SetUnhandledExceptionFilter(&TopLevelExcepFilter);
__try
{
__asm int 3
}
__except (SEHander(GetExceptionInformation()))
{
}
system("Pause");
return 0;
}
我们分别注册了VEH、VCH、
TopLevalEH 、SEH,我们看下结果:
// SEHTest.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
//
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
//
LONG __stdcall VEHandler(
EXCEPTION_POINTERS *ExceptionInfo
)
{
printf("VEHandler\n");
return EXCEPTION_CONTINUE_SEARCH;
}
LONG __stdcall VCHandler(
EXCEPTION_POINTERS *ExceptionInfo
)
{
printf("VCHandler\n");
ExceptionInfo->ContextRecord->Eip++;
return EXCEPTION_CONTINUE_EXECUTION;
}
//
LONG NTAPI TopLevelExcepFilter(EXCEPTION_POINTERS *pExcepInfo)
{
printf("TopLevelEHandler\n");
return EXCEPTION_CONTINUE_EXECUTION;
}
//
LONG NTAPI SEHander(EXCEPTION_POINTERS *ExceptionInfo){
//异常处理
printf("SEHandler\n");
return EXCEPTION_CONTINUE_SEARCH;
}
int _tmain(int argc, _TCHAR* argv[])
{
AddVectoredExceptionHandler(0,VEHandler);
AddVectoredContinueHandler(0,VCHandler);
SetUnhandledExceptionFilter(&TopLevelExcepFilter);
__try
{
__asm int 3
}
__except (SEHander(GetExceptionInformation()))
{
}
system("Pause");
return 0;
}
我们分别注册了VEH、VCH、
TopLevalEH 、SEH,我们看下结果:
他们处理异常的优先级为:
调试器
VEH
SEH
UEF
VCH
为什么调试器在第一个呢?因为我们发现在VS里面调试直接接管了异常。(/手动滑稽)
为什么调试器在第一个呢?因为我们发现在VS里面调试直接接管了异常。(/手动滑稽)
(三)、
认识SEH链及处理机机制
ntdll!_TEB
+0x000 NtTib : _NT_TIB //SEH链表头指针
+0x01c EnvironmentPointer : Ptr32 Void
+0x020 ClientId : _CLIENT_ID
+0x028 ActiveRpcHandle : Ptr32 Void
+0x02c ThreadLocalStoragePointer : Ptr32 Void
+0x030 ProcessEnvironmentBlock : Ptr32 _PEB
+0x034 LastErrorValue : Uint4B
+0x038 CountOfOwnedCriticalSections : Uint4B
+0x03c CsrClientThread : Ptr32 Void
+0x040 Win32ThreadInfo : Ptr32 Void
+0x044 User32Reserved : [26] Uint4B
+0x0ac UserReserved : [5] Uint4B
+0x0c0 WOW32Reserved : Ptr32 Void
+0x0c4 CurrentLocale : Uint4B
+0x0c8 FpSoftwareStatusRegister : Uint4B
+0x0cc SystemReserved1 : [54] Ptr32 Void
+0x1a4 ExceptionCode : Int4B
+0x1a8 ActivationContextStack : _ACTIVATION_CONTEXT_STACK
+0x1bc SpareBytes1 : [24] UChar
+0x1d4 GdiTebBatch : _GDI_TEB_BATCH
+0x6b4 RealClientId : _CLIENT_ID
+0x6bc GdiCachedProcessHandle : Ptr32 Void
+0x6c0 GdiClientPID : Uint4B
+0x6c4 GdiClientTID : Uint4B
+0x6c8 GdiThreadLocalInfo : Ptr32 Void
+0x6cc Win32ClientInfo : [62] Uint4B
+0x7c4 glDispatchTable : [233] Ptr32 Void
+0xb68 glReserved1 : [29] Uint4B
+0xbdc glReserved2 : Ptr32 Void
+0xbe0 glSectionInfo : Ptr32 Void
+0xbe4 glSection : Ptr32 Void
+0xbe8 glTable : Ptr32 Void
+0xbec glCurrentRC : Ptr32 Void
+0xbf0 glContext : Ptr32 Void
+0xbf4 LastStatusValue : Uint4B
+0xbf8 StaticUnicodeString : _UNICODE_STRING
+0xc00 StaticUnicodeBuffer : [261] Uint2B
+0xe0c DeallocationStack : Ptr32 Void
+0xe10 TlsSlots : [64] Ptr32 Void
+0xf10 TlsLinks : _LIST_ENTRY
+0xf18 Vdm : Ptr32 Void
+0xf1c ReservedForNtRpc : Ptr32 Void
+0xf20 DbgSsReserved : [2] Ptr32 Void
+0xf28 HardErrorsAreDisabled : Uint4B
+0xf2c Instrumentation : [16] Ptr32 Void
+0xf6c WinSockData : Ptr32 Void
+0xf70 GdiBatchCount : Uint4B
+0xf74 InDbgPrint : UChar
+0xf75 FreeStackOnTermination : UChar
+0xf76 HasFiberData : UChar
+0xf77 IdealProcessor : UChar
+0xf78 Spare3 : Uint4B
+0xf7c ReservedForPerf : Ptr32 Void
+0xf80 ReservedForOle : Ptr32 Void
+0xf84 WaitingOnLoaderLock : Uint4B
+0xf88 Wx86Thread : _Wx86ThreadState
+0xf94 TlsExpansionSlots : Ptr32 Ptr32 Void
+0xf98 ImpersonationLocale : Uint4B
+0xf9c IsImpersonating : Uint4B
+0xfa0 NlsCache : Ptr32 Void
+0xfa4 pShimData : Ptr32 Void
+0xfa8 HeapVirtualAffinity : Uint4B
+0xfac CurrentTransactionHandle : Ptr32 Void
+0xfb0 ActiveFrame : Ptr32 _TEB_ACTIVE_FRAME
+0xfb4 SafeThunkCall : UChar
+0xfb5 BooleanSpare : [3] UChar typedef struct _NT_TIB {
struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList;
PVOID StackBase;
PVOID StackLimit;
PVOID SubSystemTib;
#if defined(_MSC_EXTENSIONS)
union {
PVOID FiberData;
DWORD Version;
};
#else
PVOID FiberData;
#endif
PVOID ArbitraryUserPointer;
struct _NT_TIB *Self;
} NT_TIB;
typedef NT_TIB *PNT_TIB;
如上述代码所示 结构体成员 ExceptionList 即为SEH链的头部指针
So、 fs:[0]即为SEH链的指针,我们接着看对于SEH链的定义:
typedef struct _EXCEPTION_REGISTRATION_RECORD {
struct _EXCEPTION_REGISTRATION_RECORD *Next;
PEXCEPTION_ROUTINE Handler;
} EXCEPTION_REGISTRATION_RECORD;
第一个成员 Next 为指向下一个链表的指针,直到遇到 0xFFFFFFFF 结束,而结构体成员 Handler 为SEH的异常处理函数指针,我们接着看它对于
SEH异常处理函数 EXCEPTION_ROUTINE 的定义:
typedef
_IRQL_requires_same_
_Function_class_(EXCEPTION_ROUTINE)
EXCEPTION_DISPOSITION
NTAPI
EXCEPTION_ROUTINE (
_Inout_ struct _EXCEPTION_RECORD *ExceptionRecord,
_In_ PVOID EstablisherFrame,
_Inout_ struct _CONTEXT *ContextRecord,
_In_ PVOID DispatcherContext
);
可以看到它对于SEH定义了以 EXCEPTION_DISPOSITION 为返回值的回调函数,我们接着查看它们的定义:
typedef enum _EXCEPTION_DISPOSITION {
ExceptionContinueExecution, //继续执行异常代码
ExceptionContinueSearch, //运行下一个异常处理器
ExceptionNestedException, //在OS内部使用
ExceptionCollidedUnwind //在OS内部使用
} EXCEPTION_DISPOSITION;
接着我们整理下它的异常处理过程:
typedef struct _EXCEPTION_REGISTRATION_RECORD {
struct _EXCEPTION_REGISTRATION_RECORD *Next;
PEXCEPTION_ROUTINE Handler;
} EXCEPTION_REGISTRATION_RECORD;
第一个成员 Next 为指向下一个链表的指针,直到遇到 0xFFFFFFFF 结束,而结构体成员 Handler 为SEH的异常处理函数指针,我们接着看它对于
SEH异常处理函数 EXCEPTION_ROUTINE 的定义:
typedef
_IRQL_requires_same_
_Function_class_(EXCEPTION_ROUTINE)
EXCEPTION_DISPOSITION
NTAPI
EXCEPTION_ROUTINE (
_Inout_ struct _EXCEPTION_RECORD *ExceptionRecord,
_In_ PVOID EstablisherFrame,
_Inout_ struct _CONTEXT *ContextRecord,
_In_ PVOID DispatcherContext
);
可以看到它对于SEH定义了以 EXCEPTION_DISPOSITION 为返回值的回调函数,我们接着查看它们的定义:
typedef enum _EXCEPTION_DISPOSITION {
ExceptionContinueExecution, //继续执行异常代码
ExceptionContinueSearch, //运行下一个异常处理器
ExceptionNestedException, //在OS内部使用
ExceptionCollidedUnwind //在OS内部使用
} EXCEPTION_DISPOSITION;
接着我们整理下它的异常处理过程:
typedef
_IRQL_requires_same_
_Function_class_(EXCEPTION_ROUTINE)
EXCEPTION_DISPOSITION
NTAPI
EXCEPTION_ROUTINE (
_Inout_ struct _EXCEPTION_RECORD *ExceptionRecord,
_In_ PVOID EstablisherFrame,
_Inout_ struct _CONTEXT *ContextRecord,
_In_ PVOID DispatcherContext
);
可以看到它对于SEH定义了以 EXCEPTION_DISPOSITION 为返回值的回调函数,我们接着查看它们的定义:
typedef enum _EXCEPTION_DISPOSITION {
ExceptionContinueExecution, //继续执行异常代码
ExceptionContinueSearch, //运行下一个异常处理器
ExceptionNestedException, //在OS内部使用
ExceptionCollidedUnwind //在OS内部使用
} EXCEPTION_DISPOSITION;
接着我们整理下它的异常处理过程:
typedef enum _EXCEPTION_DISPOSITION {
ExceptionContinueExecution, //继续执行异常代码
ExceptionContinueSearch, //运行下一个异常处理器
ExceptionNestedException, //在OS内部使用
ExceptionCollidedUnwind //在OS内部使用
} EXCEPTION_DISPOSITION;
接着我们整理下它的异常处理过程:
(四)、
SEH的注册及SEH的删除
EXCEPTION_DISPOSITION NTAPI _except_handler(
_Inout_ struct _EXCEPTION_RECORD *ExceptionRecord, //指向包含异常信息的EXCEPTION_RECORD结构
_In_ PVOID EstablisherFrame, //指向该异常相关的EXCEPTION_REGISTRATION结构
_Inout_ struct _CONTEXT *ContextRecord, //指向线程环境CONTEXT结构的指针
_In_ PVOID DispatcherContext)
push @_except_handler ;异常处理器
push dwod ptr fs:[0] ;取出 SEH链表头
mov dwod ptr fs:[0],esp ;添加链表
卸载SEH:
push @_except_handler ;异常处理器
push dwod ptr fs:[0] ;取出 SEH链表头
mov dwod ptr fs:[0],esp ;添加链表
pop dword ptr fs:[0] ;还原链表头
add esp,4 ;删除 异常处理器
这些操作很简单,很多前辈们帖子里都有,代码可能不一样,反正是一个意思就行。
pop dword ptr fs:[0] ;还原链表头
add esp,4 ;删除 异常处理器
这些操作很简单,很多前辈们帖子里都有,代码可能不一样,反正是一个意思就行。
(五)、
异常的种类和常见的异常代码
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
上传的附件: