深入分析Win32k系统调用过滤机制

深入分析Win32k系统调用过滤机制

前言

Windows内核漏洞的利用具有高风险,经常用于浏览器沙箱逃逸。许多年以来,发现的大多数漏洞都是来源于Win32k.sys驱动,它负责处理来自GDI32.DLL和user32.dll的调用。为了缓解这些漏洞,微软在window10上实现了Win32系统调用过滤.总体思路是在进程入口处尝试阻止大量的发往win32.sys的系统调用,以便阻止未知的漏洞利用。我没有找到实现的相关细节,也不确定效果如何。我发现的唯一资料就是由Peter Hlavaty发表在 Rainbow Over the Windows的相关文章。

系统调用101

我理解过滤技术的第一个方法就是研究系统调用是如何执行。分析是基于Windows 10周年更新的64位版本。通常,系统调用在gdi32.dll或者user32.dll的函数中被初始化,最终会在win32u.dll中调用真实的系统调用。但是,我们可以直接使用汇编来显示系统调用,在以下的POC中,我查询到系统调用号为0x119E的WIN32K函数是NtGdiDdDDICreateAllocation。所以我简单地创建下面的测试应用程序:

系统调用101

下图是NtGdiDdDDICreateAllocation的汇编代码:

http://p2.qhimg.com/t01cd53b49ccf713ba5.png

当运行syscall指令后,将转入内核模式执行.真实的系统调用位于NT!KiSystemStartService。但是,由于有大量的系统调用,所以我们需要在调试器中设置一个条件断点:

调试器

运行POC并开启断点

运行POC并开启断点

首先显示的系统调用号就是我们提供的0x119E,其实参数1,2,3,4保存在寄存器RCX,RDX,R8和R9中。在IDA中查看相关代码:

在IDA中查看相关代码

查阅代码,我们发现一个有趣的问题:RBX寄存器的内容是什么,这又是从何而来。尝试引用KiSystemServiceStart,我们发现 RBX在以下函数中被设置:

RBX在以下函数中被设置

MOV RBX GS:188将Win32SyscallFilter.exe的内核线程结构载入RBX中,验证如下:

RBX

研究算法

接下来的问题是RBX + 0x78代表什么,事实证明,它代表一系列的标志位。下图引用的两个标志是GuiThread和RestrictedGuiThread,它们分别位于标志的第6位和和19位。

在我们的例子中,标志位的值如下:

两个标志是GuiThread和RestrictedGuiThread

由于线程不是一个GUI线程,所以会重定向的将它转换成一个GUI线程,然后返回相同的指针。继续执行会发生:

Win32kSyscallFilter并没有做任何事。但是接下来的检查很有趣。RestrictedGuiThread标志指明,如果启用系统调用过滤,会在进程级别上进行检测:

两个标志是GuiThread和RestrictedGuiThread

因此,对于当前的进程和线程,系统调用过滤没有启用。查看进一步执行,将体现出这个标志位的重要性:

两个标志是GuiThread和RestrictedGuiThread

如果开启了系统调用过滤功能,KeServiceDescriptorTableFilter将取代KeServiceDescriptorTableShadow,如果没有开启过滤,则将使用KeServiceDescriptorTableShadow。接下来要观察系统调用表的使用,如下图所示:

两个标志是GuiThread和RestrictedGuiThread

在经过运算后,RDI包含系统调用数目。在WIN32K系统的情况下,它的值是0x20。所以,取决于系统调用过滤是否开启,不同的表会被载入R10.这两个选项是:

被载入R10.这两个选项

然后该表将转入真实的函数调用:

被载入R10.这两个选项

跟随以上的算法,我们在调试器中找到:

所以这很显然,系统调用号通过负偏移指向W32pServiceTable结构,然后指向真实的NtGdiDdDDICreateAllocation函数。这是非常好的,但是如果开启了系统调用过滤,会有什么区别呢,这可以使用W32pServiceTableFilter来进行验证:

使用W32pServiceTableFilter来进行验证

我们看到在此之前并没有什么区别,这是因为NtGdiDdDDiCreateAllocation并不是过滤的API之一,如果我们选择其他的系统调用,比如NtGdiDdDDiCreateAllocation,它的系统调用号是0x117E。我们基于是否启用系统调用过滤来对比以下两个输出。

首先是未启用系统调用过滤的:

使用W32pServiceTableFilter来进行验证

然后是启用系统调用过滤:

启用系统调用过滤

我们发现,如果开启了系统调用过滤功能,系统调用是不允许的另一个函数被调用的。该过滤函数验证是否启用系统调用过滤并简单的结束系统调用。

利用的结果

现在我们了解了系统调用过滤是如何工作的,我们需要研究它是如何防止内核漏洞利用的。首先要看看它保护的是什么进程,到目前为止,仅仅是微软Edge可以启用这个功能,目前第三方程序没有可以启用它的接口。这意味着系统调用过滤仅仅关心Microsoft Edge漏洞并且仅限于内核漏洞。下面我们可以看到MicrosoftEdgeCP.exe的进程结构,并启用了系统调用过滤:

MicrosoftEdgeCP.exe的进程结构,并启用了系统调用过滤

回顾我早期文章,关于重新启用tagWND对象作为读写原语的用法的,我想知道是否使用这个方法的任意系统调用都会被过滤,在那个方法中使用的内核模式的函数是:

 
 
  1. NtUserCreateWindowEx 
  2. NtUserDestroyWindow 
  3. NtUserSetWindowLongPtr 
  4. NtUserDefSetText 
  5. NtUserInternalGetWindowText 
  6. NtUserMessageCall 

在win32k.sys中没有stub_*方法的函数意味着不会被过滤。结论就是Win32K系统调用过滤不会阻止可能导致漏洞的系统调用,但是当系统调用触发一个漏洞时,它一定会阻止,可能是write-that-wherer或者是一个缓冲区溢出。WIN32K系统调用过滤所提供的保护是巧妙的,但关键在于是否使用了系统调用来触发漏洞。

作者:胖胖秦
来源:51CTO

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
一:SSDT表的hook检测和恢复 ~!~~~ 二:IDT表的hook检测和恢复 ~~~~~~(idt多处理器的恢复没处理,自己机器是单核的,没得搞,不过多核的列举可以) 三:系统加载驱动模块的检测 通过使用一个全局hash表(以DRIVEROBJECT为对象)来使用以下的方法来存储得到的结果,最终显示出来 1.常规的ZwQuerySystemInformation来列举 2通过打开驱动对象目录来列举 3搜索内核空间匹配驱动的特征来列举(这个功能里面我自己的主机一运行就死机,别的机器都没事,手动设置热键来蓝屏都不行,没dump没法分析,哎,郁闷) 4从本驱动的Modulelist开始遍历来列举驱动 四:进程的列举和进程所加载的dll检测 采用以下方法来列举进程: 1ZwQuerySystemInformation参数SystemProcessesAndThreadsInformation来枚举 2进程EPROCESS 结构的Activelist遍历来枚举 3通过解析句柄表来枚举进程 4通过Handletablelisthead枚举进程 5进程创建时都会向csrss来注册,从这个进程里面句柄表来枚举进程 6通过自身进程的HANDLETABLE来枚举进程 7通过EPROCESS的SessionProcessLinks来枚举进程 8通过EPROCESS ---VM---WorkingSetExpansionLinks获取进程 9暴力搜索内存MmSystemRangeStart以上查找PROCESS对象 进程操作: 进程的唤醒和暂停通过获取PsSuspendProcess和PsResumeProcess来操作的 进程结束通过进程空间清0和插入apc。 采用以下方法查找DLL: 1遍历VAD来查找dll 2挂靠到对应的进程查找InLoadOrderLinks来枚举dll 3暴力搜索对应进程空间查找pe特征来枚举dll DLL的操作: Dll的卸载是通过MmUnmapViewOfSection和MmmapViewOfSection(从sdt表中相应函数搜索到的)来实现的(本来想直接清0 dll空间,有时行有时不行)(只要将这个进程的ntdll卸载了,进程就结束了,一个好的杀进程的办法撒,绿色环保无污染),注入dll使用的是插入apc实现的。(注入的dll必须是realse版的。Debug版会出现***错误,全局dll注入貌似也是)插入apc效果不是很好,要有线程有告警状态才执行。 五:线程信息的检测 遍历ThreadList来枚举线程 线程的暂停和唤醒都是通过反汇编获取PsResumeThread和PsSuspendThread直接从r3传来ETHREAD来操作的,通过插入APC来结束线程 六:shadow sdt表的hook检测与恢复 没有采用pdb来解决函数名问题,直接写入xp和03的shandow表函数名(主要是自己的网不稳定,连windbg有时都连不上微软) 七:系统所有的过滤驱动的检测 查看各device下是否挂接有驱动之类的,可直接卸载 八:系统常用回调历程的检测和清除 只检查了PsSetLoadImageNotifyRoutine PsSetCreateThreadNotifyRoutine PsSetCreateProcessNotifyRoutine CmRegisterCallback这几个,至于那个什么shutdown回调不知道是啥玩意,就没搞了,有知道的顺便告诉我下撒,谢谢 九:文件系统fat和ntfs的分发函数检测 直接反汇编fat和ntfs的DriverEntry得到对应的填充分发的偏移,然后和当前已经运行的文件系统的分发相比是否被hook,并附带恢复 十:文件查看功能 自己解析ntfs和fat的结构,来实现列举文件和直接写磁盘删除。附带有普通的删除和发生IRP来删除。不过这里面有点问题,ntfs删除有时把目录给搞坏了,大家凑合着吧, Ntfs网上删除这些操作的代码不多,就是sudami大大的利用ntfs-3g来实现的,看了下,太多了,充满了结构。然后自己对照着系统删除文件时目录的变化来自己实现的。只处理了$BITMAP对应的位清除,父目录的对应文件的索引项的覆盖,删除文件对应的filerecord清0. 另外偷懒时间都没处理,呵呵,y的,一个破时间都都搞好几个字节移来移去的。 十一:常用内核模块钩子的检测和恢复 这里只检测了主要的内核模块nkrnlpa**.exe的.win32k.sys,hal.dll,比对它们的原始文件从而查找eat inline hook,iat hook ,和inline hook。Inline是从TEXT段开始一段位置开始比较的。(有点慢貌似,等待显示扫描完成就好了) 十二:应用层进程所加载dll的钩子 应用层钩子检测和内核模块钩子检测原理一样,不过为了能读写别的进程的空间,并没有使用openprocess去打开进程,而是通过KiattachProcess挂靠到当前进程,然后通过在r0直接读写进程空间的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值