[2021]漫游微软屎山 win32kfull.sys (一) ssdt和shadow ssdt huoji SSDT HOOK,微软,win32kfull,shaodw ssdt,屎山 2021-09-04 1645 次浏览 1 次点赞 微软的win32kfull.sys这玩意绝对是,微软整个操作系统上漏洞最多的驱动之一,因为他的代码是从win3.1一路贴屎山贴过来的,在如今windows 20h2最新系统上,你任然可以看到某些莫名其妙的函数 ![](https://key08.com/usr/uploads/2021/09/322474017.png) lszzz zzz qqqqzzzqx等奇怪的看起来就像是屎山的代码 过去二十年,微软这玩意一直是漏洞报告的重头戏,各种本地提权、rce层出不穷,其恶劣程度堪比rpc和wmi.因此我准备单独开一系列介绍这个win32kfull.sys(本人也在持续逆向他中) ## ssdt和shadow ssdt 总所周知,x64后大部分常规API进内核的方式是syscall,当有syscall指令后,cpu会把rip指向amd64_lstar这个寄存器里面的地址,操作系统则把这个寄存器改成自己的进内核的函数,微软windows下这个r3和内核交互的函数名字叫做 kisystemcall64 而操作系统的大部分跟R3交互的API 都是一套叫做SSDT的机制控制的,并且如果是涉及到图形的api 则是放在shadow ssdt也就是sssdt,为什么要这样放,因为内核实在是没办法塞下win3.1的垃圾屎山代码.也没办法重构,所以干脆分开 sssdt通常是比普通的ssdt多0x1000个index,因此操作系统一般用这个来区分是sssdt还是ssdt ssdt表叫做 KiSystemServiceRepeat ssdt表叫做 KeServiceDescriptorTableShadow ### kisystemcall64 在kisystemcall64中操作系统会判断是否是gui线程通过kthread的结构,如果是则用KeServiceDescriptorTableShadow否则用KiSystemServiceRepeat ![](https://key08.com/usr/uploads/2021/09/228607220.png) 在处理sssdt的block里面,有个至关重要的函数 ![](https://key08.com/usr/uploads/2021/09/3235914127.png) KiConvertToGuiThread 这个所谓的KiConvertToGuiThread,其实最终调用的是PsConvertToGuiThread ![](https://key08.com/usr/uploads/2021/09/3138481558.png) ### 为什么要调用PsConvertToGuiThread PsConvertToGuiThread代码如下 ```cpp NTSTATUS NTAPI PsConvertToGuiThread(VOID) { ULONG_PTR NewStack; PVOID OldStack; PETHREAD Thread = PsGetCurrentThread(); PEPROCESS Process = PsGetCurrentProcess(); NTSTATUS Status; PAGED_CODE(); if (KeGetPreviousMode() == KernelMode) return STATUS_INVALID_PARAMETER; if (!PspW32ProcessCallout) return STATUS_ACCESS_DENIED; if (Thread->Tcb.ServiceTable != KeServiceDescriptorTable) { return STATUS_ALREADY_WIN32; } if (!Thread->Tcb.LargeStack) { NewStack = (ULONG_PTR)MmCreateKernelStack(TRUE, 0); if (!NewStack) { NtCurrentTeb()->LastErrorValue = ERROR_NOT_ENOUGH_MEMORY; return STATUS_NO_MEMORY; } KeEnterGuardedRegion(); OldStack = KeSwitchKernelStack((PVOID)NewStack, (PVOID)(NewStack - KERNEL_STACK_SIZE)); KeLeaveGuardedRegion(); MmDeleteKernelStack(OldStack, FALSE); } if (!Process->Win32Process) { Status = PspW32ProcessCallout(Process, TRUE); if (!NT_SUCCESS(Status)) return Status; } Thread->Tcb.ServiceTable = KeServiceDescriptorTableShadow; ASSERT(Thread->Tcb.Win32Thread == 0); Status = PspW32ThreadCallout(Thread, PsW32ThreadCalloutInitialize); if (!NT_SUCCESS(Status)) { Thread->Tcb.ServiceTable = KeServiceDescriptorTable; } return Status; } ``` 三个作用 1.换到一个更大的内核栈里面通过MmCreateKernelStack和KeSwitchKernelStack 2.修改tcb.ServiceTable为shadowtable 3.通过PspW32ThreadCallout设置W32THREAD这个结构 至此 这个线程就已经成为了 一个 "GUI线程" ### 为什么要是GUI线程? 其实所谓的gui线程,就是加了临界区的机制, 用于多线程之间的互相控制 在syscall进入sssdt后,这些sssdt函数会走到win32kfull.sys里面,这些函数首先就要进入临界区,win7之前的临界区都是关键临界区,也就是无论如何进入sssdt后会设置一个叫做gptiCurrent的变量,**这个gptiCurrent是之前设置的W32THREAD**,win7之后为了效率则设置成了共享临界区也就是不再设置gptiCurrent,**这些sssdt函数都默认你是gui线程** 在我之前做hypervisor的sysport hook的时候想hook sssdt表但是没有考虑到gui线程和nogui线程这个情况导致爆炸,此外也有一些记录在案的DOS漏洞也是因为这个改变导致一个非GUI线程用了SSSDT函数导致蓝屏 ## 未完待续... 有些可能有错误,欢迎评论区指出 本文由 huoji 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。 点赞 1
还不快抢沙发