目录

Win异常体系分析(2):用户VEH异常分析

添加、删除VEH回调

AddVectoredExceptionHandler

  1. 该函数签名如下所示,摘自MSDN:
// 函数原型
PVOID AddVectoredExceptionHandler(
  ULONG                       First,
  PVECTORED_EXCEPTION_HANDLER Handler
);

// 回调类型
typedef LONG (CALLBACK *PVECTORED_EXCEPTION_HANDLER)(
    PEXCEPTION_POINTERS ExceptionInfo
);

// 异常信息:异常记录、上下文记录
typedef struct _EXCEPTION_POINTERS {
    PEXCEPTION_RECORD ExceptionRecord;
    PCONTEXT         ContextRecord;
} EXCEPTION_POINTERS;
  1. 该函数是由KernelBase.dll导出,真实来源其实是NTDLL.RtlAddVectoredExceptionHandler,其导出信息如下:

/vehexception/Image/1-AddVectoredExceptionHandler%E5%AF%BC%E5%87%BA.png

  1. NTDLL.RtlAddVectoredExceptionHandler反汇编如下,其功能只是将参数3清空后,直接跳转到RtlpAddVectoredHandler

/vehexception/Image/2-RtlAddVectoredExceptionHandler%E5%8F%8D%E6%B1%87%E7%BC%96.png

RtlpAddVectoredHandler

  1. 提前剧透一下,RtlpAddVectoredHandler函数原型其实如下所示:
RtlAddVectoredExceptionHandler(ULONG First, PVOID Handler)
{
    return RtlpAddVectoredHandler(First, Handler, 0);
}

// 函数原型
RtlpAddVectoredHandler(
    int First,                 // RCX
    PVOID Handler,              // RDX
    ULONG Type                  // R8D: 0=VEH, 1=VCH
);
  1. 核心汇编流程如下:

/vehexception/Image/3-RtlpAddVectoredHandler%E6%B1%87%E7%BC%96.png

  1. 做了哪些事?可以总结如下:
  1. 分配一个0x28字节大小的VehNode,这个Node中大部分字段意义未知,唯一可以确定的是VehNode.20h处保存的是加密后的Veh回调指针
  2. 这个加密使用到了ntdll中的一个进程级的Cookie,使用它来加密回调指针,加密公式为EncPtr = ROR64(VehHandler ^ Cookie, Cookie & 3Fh)

全局对象LdrpVectorHandlerList

  1. 关于LdrpVectorHandlerList这个对象,不能简单认为其就是链表头,这是一个对象,有哪些字段内容,开始分析
  2. 在构造好一个VehNode后,必当要将其加入到全局链表中,要操作一个全局链表,必定需要加锁,所以我们找到一处加锁的代码进行分析(如下图所示):
  3. 首先取出了LdrpVectorHandlerList对象指针,之后访问其中字段内容,这里的rsi寄存器内容,在Veh路径下rsi = 0,Vch路径下rsi = 3
  4. 这里很明显,180081804处代码就是取出一个锁对象,之后调用RtlAcquireSRWLockExclusive进行加锁

/vehexception/Image/4-LdrpVectorHandlerList%E5%8A%A0%E9%94%81.png

  1. 那么到了这里,我们就可以初步推测LdrpVectorHandlerList这个对象结构如下:
LdrpVectorHandlerList:
	0x0  VehLockObject
	0x8	 Unknown1
	0x10 Unknown2
	0x16 VchLockObject
	0x20 Unknown3
	0x28 Unknown4

继续分析插入链表操作

  1. 我们目前顺利分析出了如何初始化一个VehNode、LdrpVectorHandlerList对象部分字段,那么接下来分析如何插入到全局链表,如图所示就是头插法汇编代码:
  2. 已知这里的rbx是构造好的VehNode,下面的汇编,很明显就是InsertHeadList的汇编

/vehexception/Image/5-InsertHeadList%E5%A4%B4%E6%8F%92%E6%B3%95.png

  1. 所以我们可以合理推测LdrpVectorHandlerList结构如下:
LdrpVectorHandlerList:
	0x0  VehLockObject
	0x8	 ListEntry1
	0x16 VchLockObject
	0x20 ListEntry2
  1. 继续分析得到VehNode的部分结构如下:
VehNode:
	0x0  ListEntry
	// ...
	0x20 EncryptHandler

KiUserExceptionDispatcher

  1. 该函数是R3异常分发的起始落脚点,该函数的特点就是没有返回值,如果成功处理异常调用NtContinue继续执行代码,否则调用NtRaiseException进行二次派发异常。不管是何种路径,该函数都不会执行到Ret
  2. 这里需要注意一下,1800A0C23处的代码,是不会执行的

/vehexception/Image/6-KiUserExceptionDispatcher.png

RtlDispatchException

  1. 这个函数是R3下VEH、SEH异常派发的总入口点
  2. 首先调用VEH异常回调

/vehexception/Image/7-RtlDispatchException%E8%B0%83%E7%94%A8VEH%E5%9B%9E%E8%B0%83.png

  1. RtlpCallVectoredHandlers如果返回成功,那么直接跳走,继续执行VCH回调

/vehexception/Image/8-RtlDispatchException%E8%B0%83%E7%94%A8VCH%E5%9B%9E%E8%B0%83.png

RtlpCallVectoredHandlers

  1. 取出Veh链表头

/vehexception/Image/9-%E5%8F%96%E5%87%BAVeh%E9%93%BE%E8%A1%A8%E5%A4%B4.png

  1. 该函数会在栈上构造局部变量ExceptionPointers

/vehexception/Image/10-%E6%9E%84%E9%80%A0ExceptionPointers.png

  1. 遍历Veh链表

/vehexception/Image/11-%E9%81%8D%E5%8E%86Veh%E9%93%BE%E8%A1%A8.png

  1. 解密Veh Handler

/vehexception/Image/12-%E8%A7%A3%E5%AF%86VehHandler.png

  1. 通过CFG调用Veh回调

/vehexception/Image/13-CFG%E8%B0%83%E7%94%A8Veh%E5%9B%9E%E8%B0%83.png

拓展

  1. 在加入、删除VEH回调时,涉及到了加密、解密函数指针,我们其实不用管算法是啥,有提供RtlEncodePointer、RtlDecodePointer函数,直接调用即可
异常体系分析 Windows内核