Windows NT环境下,Win32子系统提供了一套Win32 API函数以满足用户层应用程序访问更高权限的服务器组件。对于客户端的每个线程,在windows子系统进程中都有一个专门配对的服务器线程在等待客户线程的请求,一个被称为fast lpc的特殊的跨进程通信设施, 可用来在这些线程之间发送消息。与普通的线程环境切换不同的是,在这些配对的线程之间fast LPC来传递消息并不会使内核中产生一个重新调度(rescheduling)的事件,服务器线程在等待内核的抢先式线程调度器将执行权交给它以前,可以利用客户线程尚未用完的时间片来运行自己的代码。另外,利用共享的内存缓冲区,可以传递一些大的数据结构, 客户可以直接(但只读)访问关键的服务器数据结构,以便使客户和町1^(^8子系统服务器之间的线程/进程转换尽可能地减少到最少。 尽管已经提升了传统听032子系统的性能,但miceosoft公司还是决定将windows nt4.0发行版的大部分服务器组件移植到内核模式下。这些改变使得子系统可直接访问其他的windows 执行体组件,而无须付出用户模式或者内核模式转换的代价。然而,相对在相同权 限级之间的代码/数据的直接访问,用户与内核模式之间的切换效率还是显得慢;一些旧的 诡计如高速管理结构缓存在用户模式下的部分客户端地址空间上依旧保存有;另外,一些管 理结构还专门存放在用户模式下以避免Ring转换。Win32k需要通过一种方式访问到这些信 息并支持一些基本功能如windows钩子,故需要将控制权传递给用户模式端,即用户模式回调机制。用户模式回调机制允许win32k实现用户模式回调任务,如调用应用程序定义的hook函数、提供事件通知、用户模式数据拷进拷出等。本文,我们主要讨论win32k系统中的用 户模式回调涉及到的各种挑战和问题难点,并给出win32k在数据完整性设计上的某些不完 整漏洞(如用户模式下的回调)。 1.用户模式下的回调 Win32k系统中使用了大量的系统调用函数实现用户模式通知,如应用程序自身设置的钩 子函数、内核驱动的异步事件通知、数据拷贝(从用户模式进出)等。回调函数机制的实现 函数为KeuserModeCallback—个nt执行体导出的函数,类似于一个反向系统调用,定义 如下: NTSTATUS KeUserModeCallback ( IN ULONG ApiNumber, IN PVOID InputBuffer, IN ULONG InputLength, OUT PVOID *OutputBuffer, IN PULONG OutputLength ); 当win32使用用用户模式回调时,系统会调用函数KeUserModecallback并带上参数 ApiNumber,即系统需要调用的用户模式函数的索引值,此索引值存放在一个函数指针表 KernelCallbackTable,此地址在一个给定的进程中对user32.dll进行初始化期间,被拷贝 到进程环境控制块四8结构中的。 系统通过如下公式:Call backFunPointer=KernelCa11backTable[ApiIndex],取回回调 函数指针。故ApiNumber=(回调函数地址一 KernelCallbackTable) / 4。Win32k为相应的 回调函数提供了一个inputbuffer参数,并接收用户模式输出的参数outputbuffer.一个系 统调用发生后,nt! KiSystemService或者是nt! KernelCallbackTable函数会在内核线程桟上存 放一个陷阱栈(TRAP_FRAME)以保存当前线程上下文,并在返回用户模式时恢复寄存器值。 为了使通信能够准确返回到用户模式下指定的回调函数地址4&%^0如0&1^&(^函数会首 先通过线程对象使用陷阱栈信息将1叩此6^化『参数值拷贝到用户模式栈,然后创建一个新 的陷阱栈,并将桟指针£1?设置为的耵1 ! KiUserCallbackDispatcher,以替换线程对象的 TrapFramej0#,最终调用nt! kiservicexit将执行权限交给用户模式的回调派遣函数。 用户模式下的回调机制需要一个空间存放线程状态信息如陷阱栈等,在windows xp和windows 2003下,系统会自动增长内核栈长度,以确保有足够的空间使用。然而,桟空间总会被大量 的递归回调函数迅速耗尽,故Vista.windows 7系统为每个用户模式回调函数创建了一个新的内核线程桟。桟区域与桟控制结构分别如下所示: kd> dt nt!_KSTACK_AREA +0x000 FnArea : _FNSAVE_FORMAT +0x000 NpxFrame : _FXSAVE_FORMAT +0xle0 StackControl : _KERNEL_STACK_CONTROL +0xlfc CrONpxState : Uint4B +0x200 Padding : [4] Uint4B kd> dt nt!_KERNEL_STACK_CONTROL _b +0x000 PreviousTrapFrame : Ptr32 +0x000 PreviousExceptionList : Ptr32 +0x004 StackControlFlags : Uint4B +0x004 PreviousLargeStack : Pos 0, 1 Bit +0x004 PreviousSegmentsPresent : Pos 1, 1 Bit +0x004 ExpandCalloutStack : Pos 2, 1 Bit +0x008 Previous : _KERNEL_STACK_SEGMENT +0x000 StackBase : Uint4B +0x004 StackLimit : Uint4B |