深入理解SEH

KiDispatchException我们已经知晓,其内核态的异常分发主要靠检测“KiDebugRoutine”,是否拥有调试器来实现,然后根据RtlDispatchException来调用内核的SEH来处理。对于PreviousMode为userMode而言,其SEH和VEH链最后是由KiUserDispatchException来处理的,所以我们这次深入看看KiUserDispatchException到底做了什么。

在这里插入图片描述无奈没有源码,只好照搬这张看雪加密与解密的这张图了。可以看出其实KiUserDispatchException也只是一个框架,主要处理函数仍然是在RtlDispatchException。但我们先看一下KiUserDispatchException的处理流程。首先调用RtlDispatchException,检测能否被处理,若能被处理,则会调用NtContinue函数,不会再返回。若RtlDispatchException返回了false,则调用NtRaiseException,同时最后传进去false用于初始化将FirstChance赋值为0,这样就相当于第二次处理异常,同时该函数不会返回。如果第二次仍然不行,则会将该异常的flags值标记为EXECUTION_NONCONTINUABLE,然后结束分发。
最终不管是KernelMode还是UserMode最终又指向了RtlDispatchException。所以我们来看看RtlDispatchException的源码

//Exception Flags
#define EXCEPTION_NONCONTINUABLE 0x1    // Noncontinuable exception
#define EXCEPTION_UNWINDING 0x2         // Unwind is in progress
#define EXCEPTION_EXIT_UNWIND 0x4       // Exit unwind is in progress
#define EXCEPTION_STACK_INVALID 0x8     // Stack out of limits or unaligned
#define EXCEPTION_NESTED_CALL 0x10      // Nested exception handler call
#define EXCEPTION_TARGET_UNWIND 0x20    // Target unwind in progress
#define EXCEPTION_COLLIDED_UNWIND 0x40  // Collided exception handler call

//MmExecutionFlags on Win7
#define MEM_EXECUTE_OPTION_DISABLE 0x1 
#define MEM_EXECUTE_OPTION_ENABLE 0x2
#define MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION 0x4
#define MEM_EXECUTE_OPTION_PERMANENT 0x8
#define MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE 0x10
#define MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE 0x20
#define MEM_EXECUTE_OPTION_DISABLE_EXCEPTIONCHAIN_VALIDATION 0x40
#define MEM_EXECUTE_OPTION_VALID_FLAGS 0x7f

//NtGlobalFlag
#define FLG_ENABLE_CLOSE_EXCEPTIONS     0x00400000      // kernel mode only
#define FLG_ENABLE_EXCEPTION_LOGGING    0x00800000      // kernel mode only

//Part of ProcessInformationClass
#define ProcessExecuteFlags	34

typedef struct _DISPATCHER_CONTEXT {
	PEXCEPTION_REGISTRATION_RECORD RegistrationPointer;
} DISPATCHER_CONTEXT;

//
// Execute handler for exception function prototype.
//

EXCEPTION_DISPOSITION
	RtlpExecuteHandlerForException (
	IN PEXCEPTION_RECORD ExceptionRecord,
	IN PVOID EstablisherFrame,
	IN OUT PCONTEXT ContextRecord,
	IN OUT PVOID DispatcherContext,
	IN PEXCEPTION_ROUTINE ExceptionRoutine
	);

VOID
	RtlpGetStackLimits (
	OUT PULONG LowLimit,
	OUT PULONG HighLimit
	);

EXCEPTION_DISPOSITION
	RtlCallVectoredExceptionHandlers (
	IN PEXCEPTION_RECORD ExceptionRecord,
	IN OUT PCONTEXT ContextRecord
	);

EXCEPTION_DISPOSITION
	RtlCallVectoredContinueHandlers (
	IN PEXCEPTION_RECORD ExceptionRecord,
	IN OUT PCONTEXT ContextRecord
	);

PEXCEPTION_REGISTRATION_RECORD
	RtlpGetRegistrationHead (
	VOID
	);

BOOLEAN
	RtlIsValidHandler (
	IN PEXCEPTION_ROUTINE Handler,
	IN ULONG ProcessExecuteFlag
	);

BOOLEAN __stdcall RtlDispatchException(PEXCEPTION_RECORD pExcptRec, CONTEXT *pContext)
{
	BOOLEAN Completion; 
	PEXCEPTION_RECORD pExcptRec;
	EXCEPTION_REGISTRATION_RECORD *RegistrationPointerForCheck;
	EXCEPTION_REGISTRATION_RECORD *RegistrationPointer;
	EXCEPTION_REGISTRATION_RECORD *NestedRegistration;
	EXCEPTION_DISPOSITION Disposition; 
	EXCEPTION_RECORD ExceptionRecord1;
	DISPATCHER_CONTEXT DispatcherContext;
	ULONG ProcessExecuteOption;
	ULONG StackBase,StackLimit; 
	BOOLEAN IsSEHOPEnable;
	NTSTATUS status;

	Completion = FALSE;

	// 首先调用VEH异常处理例程,其返回值包括EXCEPTION_CONTINUE_EXECUTION (0xffffffff)和EXCEPTION_CONTINUE_SEARCH (0x0)两种情况
	// 这是从Windows XP开始加入的新的异常处理方式
	// 返回值不是EXCEPTION_CONTINUE_SEARCH,那么就结束异常分发过程
	if (RtlCallVectoredExceptionHandlers(pExcptRec, pContext)  !=  EXCEPTION_CONTINUE_SEARCH )
	{
		Completion = TRUE;
	}
	else
	{
		// 获取栈的内存范围
		RtlpGetStackLimits(&StackLimit, &StackBase);
		ProcessExecuteOption = 0;

		// 从fs:[0]获取SEH链的头节点 即mov eax, fs:[0]
		RegistrationPointerForCheck = RtlpGetRegistrationHead();

		// 默认假设SEHOP机制已经启用,这是一种对SEH链的安全性进行增强验证的机制,Structured Exception Handler Overwrite Protection,我们先不管
		IsSEHOPEnable = TRUE; 

		// 查询进程的ProcessExecuteFlags标志,决定是否进行SEHOP验证
		status = ZwQueryInformationProcess(NtCurrentProcess(), ProcessExecuteFlags, &ProcessExecuteOption, sizeof(ULONG), NULL) ;

		// 在查询失败,或者没有设置标志位时,进行SEHOP增强验证
		// 也就是说,只有在明确查询到禁用了SEHOP时才不会进行增强验证
		if ( NT_SUCCESS(status) 
			&& (ProcessExecuteOption & MEM_EXECUTE_OPTION_DISABLE_EXCEPTIONCHAIN_VALIDATION) )
		{
			// 若确实未开启SEHOP增强校验机制,设置此标志 此时 status == 0,
			IsSEHOPEnable = FALSE; 
		}
		else
		{
			// 否则,进行开始SEHOP验证 如果头结点是0xffffffff,表明已经没有注册的SEH,break
			if ( RegistrationPointerForCheck == -1 )
				break;

			//验证SEH链中各个结点的有效性并遍历至最后一个结点
			do
			{
				// 若发生以下情况,认为栈无效,此时不再执行基于栈的SEH处理
				    // 1.SEH节点不在栈中
				if ( (ULONG)RegistrationPointerForCheck < StackLimit 
					|| (ULONG)RegistrationPointerForCheck + 8 > StackBase
					// 2.SEH节点的位置没有按ULONG对齐
					|| (ULONG)RegistrationPointerForCheck & 3 
					// 3.Handler在栈中
					|| ((ULONG)RegistrationPointerForCheck->Handler < StackLimit || (ULONG)RegistrationPointerForCheck->Handler >= StackBase) )
				{
					pExcptRec->ExceptionFlags |= EXCEPTION_STACK_INVALID;
					goto DispatchExit;
				}
				// 取SEH链的下一个结点
				RegistrationPointerForCheck = RegistrationPointerForCheck->Next;
			}
			while ( RegistrationPointerForCheck != -1 );

			// 此时RegistrationPointerForCheck指向最后一个节点
			// 如果TEB->SameTebFlags中的RtlExceptionAttached位(第9位)被设置,但最后一个结点的Handler却不是预设的安全SEH,那么SEHOP校验不通过,不再执行任何SEHHandler
			if ((NtCurrentTeb()->SameTebFlags & 0x200) && RegistrationPointerForCheck->Handler != FinalExceptionHandler)
			{
				goto DispatchExit;
			}
		}
		
		// 从fs:[0]获取SEH链的头节点
		RegistrationPointer = RtlpGetRegistrationHead();
		NestedRegistration = NULL;

		// 遍历SEH链表执行Handler
		while ( TRUE )
		{
			if ( RegistrationPointer == -1 ) //-1表示SEH链的结束
				goto DispatchExit;

			// 若SEHOP机制未开启,则这里必须进行校验,反之则不需要,因为SEHOP机制已经验证过了
			if ( !IsSEHOPEnable )
			{
				if ( (ULONG)RegistrationPointer < StackLimit 
					|| (ULONG)RegistrationPointer + 8 > StackBase 
					|| (ULONG)RegistrationPointer & 3 
					|| ((ULONG)RegistrationPointer->Handler >= StackLimit && (ULONG)RegistrationPointer->Handler <= StackBase) )
				{
					pExcptRec->ExceptionFlags |= EXCEPTION_STACK_INVALID;
					goto DispatchExit;
				}
			}

			// 调用RtlIsValidHandler对Handler进行增强验证,也就是SafeSEH机制
			if (!RtlIsValidHandler(RegistrationPointer->Handler, ProcessExecuteOption))
			{
				pExcptRec->ExceptionFlags |= EXCEPTION_STACK_INVALID;
				goto DispatchExit;
			}

			// 执行SEHHandler
			Disposition = RtlpExecuteHandlerForException(pExcptRec, RegistrationPointer, pContext, &DispatcherContext, RegistrationPointer->Handler);
			if ( NestedRegistration == RegistrationPointer )
			{
				pExcptRec->ExceptionFlags &=  (~EXCEPTION_NESTED_CALL);
				NestedRegistration = NULL;
			}

			// 检查SEHHandler的执行结果
			switch(Disposition)
			{
			case ExceptionContinueExecution :
                //如果返回ExceptionContinueExecution,表示处理程序认为可以继续执行,但是发生异常时候的ERR的flags里为EXCEPTION_NONCONTINUABLE,则发生冲突,引发一个不可以继续的异常。
				if ((ExceptionRecord->ExceptionFlags &
					EXCEPTION_NONCONTINUABLE) != 0) {
						ExceptionRecord1.ExceptionCode = STATUS_NONCONTINUABLE_EXCEPTION;
						ExceptionRecord1.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
						ExceptionRecord1.ExceptionRecord = ExceptionRecord;
						ExceptionRecord1.NumberParameters = 0;
						RtlRaiseException(&ExceptionRecord1);

				} else {//Completion设置为true,表明完成了处理
					Completion = TRUE;
					goto DispatchExit;
				}

			case ExceptionContinueSearch ://若ERR的异常标志位表明栈无效,则直接退出
				if (ExceptionRecord->ExceptionFlags & EXCEPTION_STACK_INVALID)
					goto DispatchExit;

				break;

			case ExceptionNestedException :
				ExceptionRecord->ExceptionFlags |= EXCEPTION_NESTED_CALL;
				if (DispatcherContext.RegistrationPointer > NestedRegistration) {
					NestedRegistration = DispatcherContext.RegistrationPointer;
				}

				break;

			default ://若以上选项都不是,表明返回的值是个错误的值,引发一个STATUS_INVALID_DISPOSITION无效选项的异常,并且flag设置为不可继续执行标记
				ExceptionRecord1.ExceptionCode = STATUS_INVALID_DISPOSITION;
				ExceptionRecord1.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
				ExceptionRecord1.ExceptionRecord = ExceptionRecord;
				ExceptionRecord1.NumberParameters = 0;
				RtlRaiseException(&ExceptionRecord1);
				break;
			}

			// 取SEH链的下一个结点
			RegistrationPointer = RegistrationPointer->Next;           // Next
		}
	}

DispatchExit:

	// 调用VEH的ContinueHandler
	// 只要RtlDispatchException函数正常返回,那么ContinueHandler总会在SEH执行完毕后被调用
	RtlCallVectoredContinueHandlers(pExcptRec, pContext);
	return Completion;
}

整理一下思路,Completion作为返回值被送回到KiUserDispatchException,Completion只会有两种控制流会被设置为true。

  • 调用RtlCallVectoredExceptionHandlers,若处理得到的返回值不是EXCEPTION_CONTINUE_SEARCH,即表明找到了对应的处理函数,则VEH处理了该异常
  • 在SEHOP没问题的前提下,遍历SEH链表,当RtlpExecuteHandlerForException的返回值dispositionExceptionContinueExecution(继续执行)并且与当初引发异常时的ExceptionFlags不冲突,那么则会将Completion设置为true,表明成功处理了异常。

接下来再理一遍RtlDispatchException的过程,加深印象。
首先分发给RtlCallVectoredExceptionHandlers处理,若成功处理,则结束成功返回。
不然就会进行SEH的遍历。因为SEH是基于栈的,所以SEHOP(结构化Overwrite Protextion)就应运而生,为了防止利用者篡改SEH链而进行的设置,从xp后开始加上了这个设置。默认是SEHOP是开启的。SEHOP的标志位可由ZwQueryInformationProcess获得,通过检验MEM_EXECUTE_OPTION_DISABLE_EXCEPTIONCHAIN_VALIDATION标志位,确认确实没开启SEHOP选项,那么就不需要SEHOP验证。否则进入验证的循环。通过遍历SEH链,对每一个结点都要进行check。check主要进行以下的操作

  • 检验该ERR处于的地址是否在栈中,栈的base和limit在初期通过fs的TIB获取,不记得的再找下TIB的数据结构看看。注意此时的limit在低地址,base在高地址,因为栈是向低地址增长的。
  • 并且栈是按4字节对齐,所以对齐操作也会check。
  • 并且还要检测handler的地址,handler的地址应该位于代码段,而不是布置到栈上,若布置到栈上,很有可能就是利用者将数据放到栈上了。
  • 对于最后一个结点也会做check,主要是判断TEB->SameTebFlags 和 FinalExceptionFilter是否一致,确保最后一个结点的handler未被篡改

若在上述过程中,有任一结点不满足要求,都会不在进行基于栈的SEH操作,直接返回。

到这里,开了SEHOP的check就完成了。接着会再次遍历该SEH链,对于未开启SEHOP的程序,RtlDispatchException仍会先检测结点的合法性RtlIsValidHandler会对每一个结点强制进行增强验证,通过后会调用RtlpExecuteHandlerForException,传递参数并且调用注册的SEH函数,其返回值作为判别的标准。对于返回值为ExceptionContinueExecution的,再检测完确实不是一个不可继续执行的异常后,就表明成功处理了,否则会引发一个不可执行的异常。对于返回值是ExceptionContinueSearch,则会继续遍历下一个结点。若返回值是一个异常数值,则会调用一个STATUS_INVALID_DISPOSITION异常。最终函数返回前会调用RtlCallVectoredContinueHandlers,但暂时还不清楚这样的目的。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值