相信越来越多的人都喜欢把驱动代码放到Pool里执行,但由此会产生一个问题,在Pool里的驱动代码是不能出异常的,所有的异常处理都会失效,在有些我们不得不使用异常,如操作Ring3内存,这时我们就必须解决掉这个问题。
X86和x64在异常处理上稍有一些不同,先来看x86上的异常处理。
系统捕获到异常后会执行到函数RtlDispatchException,函数RtlDispatchException再调用RtlLookupFunctionEntry,RtlLookupFunctionEntry函数再调用RtlLookupFunctionTable。RtlLookupFunctionTable这个函数的作用是在PsLoadedModuleList链在根据异常的EIP地址找到发生异常的模块。由于Pool里执行的代码不属于任何一个模块,这个函数就会返回失败,异常处理就会失去作用,产生一个蓝屏。
解决方案有两种:
方法1.创建一个KLDR_DATA_TABLE_ENTRY结构体,在此结构体的DllBase中填入Pool Code的起始地址,在结构体的SizeOfImage中填入代码块长度,然后把这个结体体插入到PsLoadedModuleList链中,这种方法的缺点是在驱动列表中添加了一个项,这样Pool Code就变成可见了。
方法2.Inline Hook函数RtlLookupFunctionEntry或RtlLookupFunctionTable,在内部判断如果异常在我们的Pool Code里就返回正常的值。
再来看下X64,在x64位下函数调用还是与x86下的调用一致,只是在RtlLookupFunctionTable函数里有一些不同,在x64下,系统并不直接去枚举PsLoadedModuleList链,而是先去枚举快速缓存链表PsInvertedFunctionTable,只是当PsInvertedFunctionTable缓存表已经被放满了(Overflow位),放不下的时候才去枚举PsLoadedModuleList链。
另一方面x64位系统的异常表示方式与x86有所不同,x86是直接在原代码中插入异常处理代码,而x64位下在IMAGE_OPTIONAL_HEADER.IMAGE_DATA_DIRECTORY[3]的Exception table中表示了怎么处理异常。
对于x64系统下,由于PatchGuard技术,我们不能直接HookRtlLookupFunctionEntry或RtlLookupFunctionTable函数,所以我们只能从数据上下手。也有两种方法:
方法1。首先构造Pool Code的Exception table表,x64下不管哪种方法都是需要的,对于将整个驱动作为Pool Code的可以直接使用IMAGE_OPTIONAL_HEADER.IMAGE_DATA_DIRECTORY[3]中的Exception table,然后在PsInvertedFunctionTable中插入我们的驱动的异常表(PsInvertedFunctionTable结构比较简单,在wrk中有定义,比较容易实现),PsInvertedFunctionTable表是未导出的,可以在x64下导出的函数RtlLookupFunctionEntry中搜索特征得到。
方法2。创建一个KLDR_DATA_TABLE_ENTRY结构体,在此结构体的DllBase中填入Pool Code的起始地址,在结构体的SizeOfImage中填入代码块长度,然后把这个结体体插入到PsLoadedModuleList链中,然后再找到PsInvertedFunctionTable结构体,将其中的Overflow位修改成1,表示PsInvertedFunctionTable已经满了,这样RtlLookupFunctionTable就会在查找PsInvertedFunctionTable的基础上再枚举PsLoadedModuleList链。