概述
在第一部分的结尾,我们有了一个小型反Rootkit驱动,可以检测手动映射到无后备内存的恶意驱动。本篇文章将介绍针对这种反Rootkit的规避方法,以及如何检测这些规避。
检测1:检测驱动"踩踏"
驱动踩踏是将Rootkit加载到现有驱动内存之上的技术。检测方法很简单——比较驱动在磁盘上的.text节与内存中的.text节。
实现步骤:
- 遍历
\Driver目录获取PDRIVER_OBJECT - 获取驱动服务名称并在注册表中查找路径
- 读取磁盘上的镜像文件
- 使用
RtlCompareMemory比较磁盘与内存中的.text节 - 如果不同,则检测到踩踏
注意:如果mapper使用
.data或.rdata节而非.text节,则需要额外的检测方法。
Windows线程内部机制:句柄表与PspCidTable
unKover中检查线程起始地址的技术都使用PsLookupThreadByThreadId,这是ntoskrnl.exe导出的例程。通过IDA反编译可以看到它内部调用了PspReferenceCidTableEntry,该函数引用了全局符号PspCidTable。
PspCidTable是一个句柄表,包含线程和进程的句柄。这就是为什么进程和线程ID永远不会重复——它们共享同一个ID池。
漏洞利用
如果ExpLookupHandleTableEntry找不到查询的ID,函数返回NULL。这就是攻击unKover的方法:如果从PspCidTable中移除Rootkit的线程ID,任何依赖此表的函数都将找不到该线程。
定位PspCidTable
两种方法:
- 签名扫描: 扫描
PspReferenceCidTableEntry函数中的mov rbp, cs:指令 - KeCapturePersistentThreadState: 使用未文档化的Windows内核函数检索包含解密
KdDebuggerDataBlock的小型内存转储
移除句柄表条目
扫描ExMapHandleToPointer、ExpLookupHandleTableEntry和ExDestroyHandle,使用简单函数调用即可移除条目。
检测2:通过不一致性检测篡改
检测篡改的通用策略是检查不一致性。
遍历进程的线程链表
每个进程在内核中表示为KPROCESS/EPROCESS对象,包含ThreadListHead链表。所有内核驱动默认运行在PID 4的系统进程下。
通过KTHREAD结构中的ThreadListEntry(偏移0x2f8)遍历链表,枚举所有系统进程下的驱动线程。如果发现某个线程ID无法通过PsLookupThreadByThreadId找到,则找到了隐藏线程。
展望
在用户态恶意软件领域,当线程回调开始检测各种代码注入技术时,人们发现无线程(threadless)是出路。由于unKover严重依赖线程检测,第三部分将实现无线程Rootkit。
