2014年 9月30日,旧金山——微软公司首次公开了下一代操作系统 Windows10,并且于10月1日提供了适用于PC的早期技术预览版下载。此次Windows10系统最吸引人的地方在于,它提供了跨平台的支持,从Xbox到PC、手机、平板和其它电子设备。另外,微软还将打通现有的应用商店,开发者只要写一个应用,就能轻松地将其部署到不同类型的设备上,这对于当前 Windows Phone匮乏的生态系统将极有益处。
Windows 10技术预览版发布后,我第一时间将系统从 Windows 8.1升级到了Windows 10。在这里,我不想去评论新的开始菜单及其他使用体验,这种评测文章俯拾皆是,基于我们内核研究者与黑防杂志的定位,我更想评测一下新系统的内核变化,展望一下安全软件与Rootkit怎样在新战场上“开疆拓土”。
安装完成后(保留设置与安装软件的升级),给我的第一印象不是开始菜单,而是原系统中运行正常的 360安全卫士和无线网卡皆不能正常运行了,这说明内核的变化导致了驱动不能在新系统中正常运行,所以我们还是很有必要去探索一下。
首先说一下调试方法。我的习惯是在 Windows XP虚拟机中使用 Windbg进行本地调试;
在 Windows 7/8虚拟机中使用VirtualKD+Windbg进行双机调试。对于崭新的Windows 10系统,我选择了最稳妥的方式,即在虚拟机中添加一个串口进行调试,不懂的朋友请自行上网搜索。值得一提的是,需要注意虚拟机默认的打印机已经占用了一个串口,我的方法是先删除这个打印机再添加串口。本文所有的调试信息均来自 Windows 10系统,为节约版面,只刊登出部分调试信息,我随文附送了一个文本文档,里面包含完整的调试信息。我们将调试结果与 Windows 7系统相比较,因为大多数用户将从Windows 7升至Windows 10。
切入内核指令 INT 0x2E/SYSENTER及内核入口点KiFastCallEntry对于从前的Windows系统,使用中断描述符表(IDT)中的0x2E项切入内核:
kd !idt –a …… 2e: 81b72dbe nt!KiSystemService kd u 81b72dbe l30 nt!KiSystemService: …… 81b72e48 e9f5000000 jmp nt!KiFastCallEntry+0xa2 (81b72f42)
可以看到,0x2E项对应的是KiSystemService例程,反汇编KiSystemService发现,它跳转到了入口点 KiFastCallEntry。大约自从Windows 2000系统以后,就开始使用SYSENTER指令了,但是包括Windows10系统在内,均保留了0x2E项。现代Windows系统的IDT更多的参与了与硬件相关的事物。
关于SYSENTER相关原理,我们曾经多次使用,从而找到KiFastCallEntry的地址。通过RDMSR与 WRMSR指令可以对 IA32_SYSENTER_CS、IA32_SYSENTER_ESP、IA32_SYSENTER_EIP三个 MSR寄存器进行操作,如果使用 RDMSR命令读取 IA32_SYSENTER_EIP(0x176)的内容,即可找到 KiFastCallEntry地址。
kd rdmsr 176 msr[176] = 00000000`81b72ea0 kd u 81b72ea0 nt!KiFastCallEntry: ……
调试结果证明,INT0x2E/SYSENTER部分没有发生什么变化,它们均可到达KiFastCallEntry。而 KiFastCallEntry一直是我们研究的重点内容,因为360等国内安全软件最重要的挂钩都位于 KiFastCallEntry的某处偏移当中。这里我们查看一下重点挂钩区域的变化:
kd u KiFastCallEntry+0xe6 l20 nt!KiSystemServiceAccessTeb+0x1a: 81b72f86 ff05b0060000 81b72f8c 8bf2 inc dword ptr ds:[6B0h] mov xor esi,edx 81b72f8e 33c9 ecx,ecx 81b72f90 8b570c 81b72f93 8b3f mov mov mov mov sub shr edx,dword ptr [edi+0Ch] edi,dword ptr [edi] 81b72f95 8a0c10 81b72f98 8b1487 81b72f9b 2be1 cl,byte ptr [eax+edx] edx,dword ptr [edi+eax*4] esp,ecx//360挂钩点 81b72f9d c1e902 81b72fa0 8bfc ecx,2//360挂钩点 mov test jne edi,esp//腾讯挂钩点 81b72fa2 f6457202 81b72fa6 7506 byte ptr [ebp+72h],2 nt!KiSystemServiceAccessTeb+0x42 (81b72fae) byte ptr [ebp+6Ch],1 81b72fa8 f6456c01 81b72fac 740c test je nt!KiSystemServiceCopyArguments (81b72fba) esi,dword ptr [nt!MmUserProbeAddress (81ca5d04)] 81b72fae 3b35045dca81 cmp //腾讯挂钩点 81b72fb4 0f832a020000 jae nt!KiSystemCallExit+0x77 (81b731e4)
这样,360挂钩没有成功的原因一目了然了,语句没有变,但是偏移变了,导致挂钩没有成功;至于腾讯电脑管家的挂钩,就要发生变化了,因为这两句中间已经插入了若干句。
可见,360的挂钩可移植性更好一些,我们可以关注一下这些安全软件的挂钩将来如何变化。
SSDT与Shadow SSDT(SSSDT)
SSDT与SSSDT是安全软件与Rootkit的“兵家必争之地”,也是我们的重点关注对象。
kd dds KeServiceDescriptorTable 8105fc80 8105fc84 80f1fcec nt!KiServiceTable 00000000 8105fc88 8105fc8c 000001b1 80f203b4 nt!KiArgumentTable kd dps KiServiceTable l1b1 80f1fcec 80e76800 nt!NtAccessCheck ……
相较于 Windows 7系统的 SSDT函数数量 0x191来说,多出了 0x20(32)个。要想调试 SSSDT,有两点要注意的地方:第一,不可刚一开机就调试,要等待 win32k模块初始化完成之后才可以;第二,要切换到 GUI进程之后才能调试出详细的 SSSDT函数。
kd !process 0 0//查看进程 …… PROCESS afa946c0 SessionId: 1 Cid: 0e88 Peb: 7f474000 ParentCid: 0e1c DirBase: 3fff2460 ObjectTable: a2752480 HandleCount: Data Not Accessible Image: explorer.exe kd .process afa946c0//切换进程,注意上面的 PROCESS afa946c0 Implicit process is now afa946c0 kd dps W32pServiceTable l441 8e177000 8e1717d4 win32k!NtUserGetOwnerTransformedMonitorRect ……
其中,W32pServiceTable之中保存了 0x441个 SSSDT函数,相较于Windows7系统的SSSDT函数数量 0x339来说,多出了0x108(264)个。接着我们查看所有进程,切换到一个 GUI进程,这里以 explorer.exe为例。
kd !process 0 0//查看进程 …… PROCESS afa946c0 SessionId: 1 Cid: 0e88 Peb: 7f474000 ParentCid: 0e1c DirBase: 3fff2460 ObjectTable: a2752480 HandleCount: Data Not Accessible Image: explorer.exe kd .process afa946c0//切换进程,注意上面的 PROCESS afa946c0 Implicit process is now afa946c0 kd dps W32pServiceTable l441 8e177000 8e1717d4 win32k!NtUserGetOwnerTransformedMonitorRect ……
综合上述信息,我们可以看到,本次 Windows 10系统对 SSDT的修改相对较小,最明显的地方是大幅增加了 SSSDT的函数数量(当然有些是从 Windows 8继承而来的),相信win32k的其他地方也做出了一定的修改,这就解释了系统在图形界面方面变化比较大的原因。我并没有列出所有新增的函数,因为它们不会全部与 Rootkit相关,至于哪些应该去 Hook,有待时间检验。
其他重要内核数据结构除了更改调用表(如 Hook SSDT)、更改代码(如 Inline Hook KiFastCallEntry)等常见手段之外,另一种 Rootkit常用技术当属 DKOM了,想使用 DKOM就必须清楚地认识一些内核中的重要数据结构。
1.隐藏进程
这种隐藏进程的方法是:调用PsGetCurrentProcess得到EPROCESS对象的指针,遍历ActiveProcessLinks双向链表,当找到目标 PID时,就把这个进程从链表中脱去,达到隐藏进程的目的。特别注意,PID是唯一的,而同一时间可以有多个进程名相同的进程在运行,所以要根据 PID来判别。
kd dt nt!_EPROCESS +0x000 Pcb : _KPROCESS …… +0x0b4 UniqueProcessId : Ptr32 Void//进程 ID +0x0b8 ActiveProcessLinks : _LIST_ENTRY//进程的双向链表 …… +0x170 ImageFileName : [15] UChar//进程名,Windows 7中偏移为 0x16C
2.隐藏驱动程序文件
原理基本与隐藏进程相同,请看如下调试:
kd dt nt!_DRIVER_OBJECT …… +0x014 DriverSection : Ptr32 Void//无类型指针 ……
众所周知,微软对于内核中的关键部分总是采取模糊、混淆的态度,不欲让外人所知。我们调试出的 DriverSection结构看起来是一个无类型指针,但是在众多孜孜不倦的内核黑客的探索下,揭示出了其结构中确实含有双向链表,甚至知道了在其偏移 0x2C处包含了驱动的名称。因此,我们可以如法炮制,遍历 DriverSection双向链表,匹配想要隐藏的驱动名字,从链表中将其脱去,也就达到了隐藏驱动程序的目的。本部分在 Windows 10系统中的变化不大。
结语
Windows 10系统寄托了微软对于移动互联网时代的期望,我真心期待 Windows 10系统取得巨大的成功,甚至是颠覆现有的格局。“创新之道,不破不立”,微软的创新精神深深的激励着我。
通过我们这次的初步探索可知,Windows 10系统变化最大的部分当属 win32k模块,直接决定了我们所见的 GUI部分出现了较大的变化。其他关键部分有些没变,有些进行了较小的优化调整。需要指出的是,现在的 Windows 10系统仅仅是一个早期预览版本,不排除今后还会有一些变化,甚至是较大的变化。随着时间推移,系统会逐渐成熟,安全软件与Rootkit的博弈将发生怎样的变化,让我们拭目以待。