隐藏驱动检测学习记录 —— 枚举 \Driver 与 \FileSystem 的多视角交叉验证
1. 缘起:ARK 为什么必须查隐藏驱动
之前几篇都在做进程隐藏检测(PID 暴力扫描、PspCidTable 遍历),核心哲学是多视角交叉验证——Windows 里没有绝对可信的单一视角,攻击者篡改一个数据源容易,同时篡改多个独立数据源成本急剧上升。
同样的道理完全适用于驱动。恶意驱动(Rootkit 的加载器、内核后门)想隐藏自己,最常见的手段是摘链 PsLoadedModuleList——这是 Windows 内核维护的已加载模块双向链表,摘链后普通工具(任务管理器、driverquery、甚至 NtQuerySystemInformation(SystemModuleInformation))就看不到它了。
但问题来了:驱动真的”消失”了吗?没有。只要它注册了 DRIVER_OBJECT,它就在对象管理器的命名空间里留下了痕迹。这就是本篇文章要用的第二个视角。
核心思路:检测进程时可以同时看句柄表、PspCidTable、模块链表;检测驱动,同样可以同时看「模块加载器视角」和「对象管理器视角」。
2. 驱动在内核里的两种”存在形式”
一个正常加载的驱动,会在内核里同时留下两种记录:
视角 1:模块加载器(PsLoadedModuleList)
NtQuerySystemInformation(SystemModuleInformation)返回的模块列表就来自这张链表;- SnowSword 里
GetKernelModuleList已经用它枚举驱动(路径 + 基址 + 大小); - 攻击者摘链 → 这个视角就看不到了。
视角 2:对象管理器命名空间(\Driver、\FileSystem)
- 驱动加载时会在对象管理器根目录下注册一个
DRIVER_OBJECT,名字通常是\Driver\驱动名; - 文件系统驱动注册在
\FileSystem,文件系统过滤驱动在\FileSystem\Filters; - 这套命名空间由对象管理器独立维护,与
PsLoadedModuleList是两套互不相干的机制。
攻击者如果只摘链模块链表,对象管理器目录里的 DRIVER_OBJECT 依然在——这就是检出点。
3. 视角 1 回顾:模块列表枚举(已有实现)
SnowSword 里已有实现,核心是两段式调用:
1 | ' 第一遍:查询需要的缓冲区大小 |
拿到 SYSTEM_MODULE_INFORMATION 后遍历 Modules(i),用 ImageName 取驱动路径、路径最后一段作为驱动名。
4. 视角 2:枚举 \Driver 与 \FileSystem
这是本篇文章的新内容。对象管理器命名空间像文件系统一样组织目录,核心思路分三步:
4.1 打开目录对象
用 ZwOpenDirectoryObject 打开 \Driver 或 \FileSystem 目录,拿到句柄后再用 ObReferenceObjectByHandle 转成内核态的 OBJECT_DIRECTORY* 结构指针,之后关闭句柄保留对象引用——后续操作都基于结构指针,不走句柄。
4.2 遍历哈希桶(核心逻辑)
OBJECT_DIRECTORY 内部用 37 个 HashBuckets 哈希桶组织目录项,每个桶是一条 ChainLink 链表。遍历所有桶 = 枚举目录下所有对象。这是比 ZwQueryDirectoryObject 更底层的做法(公开接口只能拿名字,直接读结构能拿到对象指针)。
关键遍历逻辑:
1 | // ⚠️ 以下为核心逻辑示意,非完整可编译代码 |
为什么是 37 个桶? 37 是质数,哈希取模时分布更均匀。但这个数字未文档化,不同 Windows 版本可能变化(Win7 确认是 37,Win10/11 需自行验证)。硬编码此值在跨版本工具中存在兼容性风险,建议通过符号解析或特征扫描动态获取。
4.3 引用计数纪律(最容易蓝屏的地方)
遍历过程中每个对象都必须 ObReferenceObject / ObDereferenceObject 配对,这是这段代码最容易出错的地方:
| 操作 | 后果 |
|---|---|
| 漏释放(非匹配对象未 Dereference) | 对象永远无法回收,内存泄漏 |
| 多释放 | 直接 BSOD |
| 匹配对象未转交引用给上层 | 对象可能被提前释放,后续访问蓝屏 |
状态流转:
1 | ObReferenceObject (引用+1) |
匹配对象把引用转交给调用者后,调用者用完必须 ObDereferenceObject,否则同样泄漏。
4.4 类型判断与基址关联
- 用
ObGetObjectType() == *IoDriverObjectType判断驱动对象,走导出 API 而非手动访问对象头,避免踩到无效对象头。 drvObj->DriverSection(LDR_DATA_TABLE_ENTRY)的DllBase与模块列表的基址比对——这一步把「对象管理器视角」和「模块加载器视角」用基址串了起来,正是交叉验证的基础。- 注意:不是每个
DRIVER_OBJECT都有有效的LDR_DATA_TABLE_ENTRY(DriverSection可能为 NULL),访问前必须判空。
5. 交叉验证:两个视角的差集
把两个视角的结果并排对比:
| 数据源 | 枚举方式 | 攻击者篡改方式 |
|---|---|---|
| 模块列表 A | SystemModuleInformation(源自 PsLoadedModuleList) |
摘链模块链表 |
| 对象目录 B | 遍历 OBJECT_DIRECTORY 哈希桶,枚举 \Driver + \FileSystem |
删除/不注册 DRIVER_OBJECT |
两个视角的数据源不同,但都能拿到驱动基址:视角 A 直接给 dwBase,视角 B 通过 DriverSection->DllBase 取。所以交叉验证可以按基址对齐:
- 遍历
\Driver、\FileSystem目录,收集所有DRIVER_OBJECT的基址; - 对每个基址与模块列表 A 的基址集合比对;
- 基址不在 A 中 = 模块列表里没有这个驱动 = 疑似摘链隐藏驱动——模块链表上看不到,但对象管理器里还留着
DRIVER_OBJECT,这就是检出点。
反过来,A 有而 B 无的条目要小心:不是所有模块都会注册 DRIVER_OBJECT(早期 boot 加载的驱动、未创建设备对象的驱动),直接报”隐藏”会误报,需要先收集正常基线再判断。
5.1 攻击者视角:如何绕过?
如果我是攻击者,怎么让这套检测失效?
- 双杀:摘链
PsLoadedModuleList+ 同时从\Driver目录删除对象(需调用ObMakeTemporaryObject或类似操作,但会留下新的痕迹); - 不注册 DRIVER_OBJECT:纯内存执行,两个视角都看不到 → 引出第九篇;
- 伪造 DriverSection->DllBase:指向一个合法模块的基址,交叉比对时被”吸附”到合法基址,产生假阴性。
检测的本质不是”找到所有坏人”,而是提高攻击者的隐藏成本。
6. 盲区与第九篇伏笔:内存暴力搜索 DRIVER_OBJECT
如果攻击者连 \Driver 对象都不注册,两个视角都会落空。但 DRIVER_OBJECT 是真实存在于内核内存里的结构,它有一个特征——结构体首部的 Type 字段(IO_TYPE_DRIVER)。所以理论上可以扫描内核内存区域,按特征寻找所有 DRIVER_OBJECT:
1 | 暴力扫描内核内存 |
7. 遇到的问题与注意事项
OBJECT_DIRECTORY是未导出结构:HashBuckets的桶数(37 个)和OBJECT_DIRECTORY_ENTRY的布局依赖系统版本,需要在自己目标系统上验证结构布局。验证方法:- Windbg:
dt nt!_OBJECT_DIRECTORY - 对比不同 Windows 版本的
_OBJECT_DIRECTORY定义 - 特征扫描:Directory 对象包含固定数量的指针数组模式
- Windbg:
引用计数准则:遍历中每个对象都要
ObReferenceObject/ObDereferenceObject配对——漏释放会引用泄漏(对象永远无法回收),多释放会直接崩溃。匹配对象把引用转交给调用者后,调用者用完必须ObDereferenceObject。DriverSection判空:不是每个DRIVER_OBJECT都有有效的LDR_DATA_TABLE_ENTRY(DriverSection可能为 NULL),访问前必须判空,否则蓝屏。误报处理:
A 有 B 无的条目需要基线(boot 驱动、无设备对象驱动都正常),不能直接当隐藏报。\FileSystem\Filters子目录:MiniFilter 的驱动对象在子目录里,漏枚举会导致视野不全。BUCKET_COUNT 硬编码风险:如前所述,37 这个数字未文档化,跨版本使用需验证。
参考
- Windows Internals, 7th Edition(对象管理器 / 驱动对象 / PsLoadedModuleList 章节)
- Microsoft Learn:ZwOpenDirectoryObject / ObReferenceObjectByHandle / ObGetObjectType / IoDriverObjectType
- 本系列前篇:SnowSword–进程隐藏检测实现、PspCidTable 进程枚举学习记录
相关项目与联系
项目SnowSword —— Windows 10/11 x64 内核分析与 Anti-Rootkit 工具,源码与发布包在 Gitee。
技术合作Windows 内核 / 驱动开发 / 逆向分析 / 兼容性开发,范围与边界见 技术合作。
联系2160752730@qq.com · QQ 2160752730 · 微信 lst_3258110
