驾驶员安全清单
本文为驱动程序开发人员提供了一个驱动程序安全清单,以帮助降低驱动程序遭到入侵的风险。 驱动程序安全性至关重要,直接影响可靠性。 当 Windows 检测到内存访问不正确时,它将关闭 OS 并显示蓝色错误屏幕。 作为 Windows 合作伙伴,你必须努力减少失败的驱动程序对客户生活中的重大影响。
有关提供安全可靠驱动程序的好处的详细信息,请参阅 驱动程序安全指南。
驱动程序安全概述
安全漏洞是任何缺陷,使攻击者能够以某种方式导致驱动程序发生故障,使攻击者能够获得未经授权的访问、操纵系统或泄露数据,从而可能导致系统崩溃或不可用。 此外,驱动程序代码中的漏洞可能允许攻击者访问内核,从而有可能危及整个 OS。
当大多数开发人员在开发驱动程序时,他们的重点是让驱动程序正常工作,而不是恶意攻击者是否会试图利用其代码中的漏洞。 然而,在驱动程序被发布后,攻击者可以尝试探测和识别安全漏洞。 开发人员必须在设计和实施阶段考虑这些问题,以尽量减少此类漏洞的可能性。 目标是在发布驱动程序之前消除所有已知的安全漏洞。
创建更安全的驱动程序需要系统架构师(有意识地考虑驱动程序的潜在威胁)、实现代码的开发人员(防御性地编码可能成为漏洞来源的常见操作)和测试团队(主动尝试发现弱点和漏洞)之间开展合作。 通过正确协调所有这些活动,驾驶员的安全性得到了显著增强。
除了避免与驱动程序被攻击相关的问题外,所述的许多步骤(例如更精确的内核内存使用)将提高驱动程序的可靠性。 这可降低支持成本,提高对产品的满意度。 完成下面清单中的任务将有助于实现所有这些目标。
安全清单:完成上述每个主题中所述的安全任务。
使用 BinSkim 和 SignTool 等工具检查驱动程序是否已准备好交付
使用 Windows 更新执行正确的发布驱动程序签名并分发驱动程序包
了解如何通过使用Microsoft易受攻击和恶意驱动程序报告中心来报告驱动程序
查看关键要点摘要
确认需要内核驱动程序
安全清单项 #1:确认需要内核驱动程序,并且风险较低的方法(如 Windows 服务或应用)不是更好的选择。
内核驱动程序位于 Windows 内核中,在内核中执行时出现问题会公开整个操作系统。 如果有任何其他选项可用,则它可能比创建新的内核驱动程序成本更低,相关风险更小。
有关使用内置 Windows 驱动程序的详细信息,请参阅 是否需要编写驱动程序?。
有关使用后台任务的相关信息,请参阅使用后台任务支持应用。
有关使用 Windows 服务的相关信息,请参阅服务。
使用驱动程序框架
安全清单项 #2:使用驱动程序框架来减小代码的大小,并提高其可靠性和安全性。
使用 Windows 驱动程序框架来减小代码的大小,并提高其可靠性和安全性。 若要快速入门,请查看使用 WDF 开发驱动程序。 有关使用风险较低的用户模式驱动程序框架(UMDF)的信息,请参阅 选择驱动程序模型。
编写旧式 Windows 驱动程序模型(WDM) 驱动程序更耗时、成本高昂,并且涉及重新创建驱动程序框架中提供的代码。
Windows 驱动程序框架 (WDF) 源代码是开放源代码,可在 GitHub 上使用。 这是 Windows 中附带的相同 WDF 源代码。 如果能够按照驱动程序和 WDF 之间的交互步骤进行操作,则可更有效地调试驱动程序。 从 https://github.com/Microsoft/Windows-Driver-Frameworks 下载它。
DMF - 驱动程序模块框架
请考虑在驱动程序项目中使用驱动程序模块框架(DMF)。 DMF 由 Microsoft Surface 团队开发,是一种框架,允许创建名为 DMF 模块的 WDF 对象。 这些 DMF 模块的代码可以在不同的驱动程序之间共享。 此外,DMF 还提供已针对驱动程序开发的 DMF 模块库,并为线程和 I/O 管理等任务提供代码重用。 DMF 模块用于将驱动程序任务封装到较小的单元中。 每个模块都是自包含的,具有自己的代码、上下文和回调,因此更易于重用。 有关详细信息,请参阅 驱动程序模块框架简介 和 GitHub 站点文档。
管理驱动程序访问控制
安全清单项 #3:查看驱动程序以确保正确控制访问权限。
管理驱动程序访问控制 - WDF
驱动程序必须防止用户不恰当地访问计算机的设备和文件。 为防止对设备和文件进行未经授权的访问,你必须:
仅在必要时命名设备对象。 命名设备对象通常是出于旧式原因所必需的,例如,如果有一个应用程序希望使用特定名称打开设备,或者你使用的是非 PNP 设备/控制设备。 请注意,若要使用 WdfDeviceCreateSymbolicLink 创建符号链接,WDK 驱动程序不需要将其 PnP 设备命名为 FDO。
安全访问设备对象和接口。
若要允许应用程序或其他 WDF 驱动程序访问 PnP 设备 PDO,应使用设备接口。 有关详细信息,请参阅使用设备接口。 设备接口充当设备堆栈 PDO 的符号链接。
控制对 PDO 的访问的更好方法是在 INF 中指定 SDDL 字符串。 如果 SDDL 字符串不在 INF 文件中,Windows 将应用默认的安全描述符。 有关详细信息,请参阅保护设备对象和设备对象的 SDDL。
有关控制访问权限的详细信息,请参阅以下内容:
名称、安全描述符和设备类 - 使设备对象易于访问... and SAFE 摘自 2017 年 1 月至 2 月由 OSR 出版的《NT 预览体验计划新闻稿》。
管理驱动程序访问控制 - WDM
如果使用 WDM 驱动程序并使用命名设备对象,则可以使用 IoCreateDeviceSecure 并指定 SDDL 来保护它。 实现 IoCreateDeviceSecure 时,请始终为 DeviceClassGuid 指定自定义类 GUID。 不应在此处指定现有的类 GUID。 这样做可能会破坏属于该类的其他设备的安全设置或兼容性。 有关详细信息,请参阅 WdmlibIoCreateDeviceSecure。
有关详细信息,请参阅以下内容:
安全标识符 (SID) 风险层次结构
以下部分介绍驱动程序代码中使用的常见 SID 的风险层次结构。 有关 SDDL 的一般信息,请参阅设备对象的 SDDL、SID 字符串和 SDDL 字符串语法。
请务必了解,如果允许较低权限调用方访问内核,则代码风险会增大。 在此摘要图中,当您允许低权限 SID 访问您的驱动程序功能时,风险会增加。
SY (System)
\/
BA (Built-in Administrators)
\/
LS (Local Service)
\/
BU (Built-in User)
\/
AC (Application Container)
遵循常规最低特权安全原则,仅配置驱动程序正常运行所需的最低访问级别。
WDM 精细 IOCTL 安全控制
Windows 中的 IOCTL (输入/输出控制)是特定于设备的输入/输出操作的系统调用。 IOCTL 由应用程序用来与设备驱动程序通信,允许它们从硬件发送命令或请求信息。 有关详细信息,请参阅 I/O 控制代码简介 和 示例 I/O 请求 - 概述。
为了在用户模式调用方发送 CTL 时进一步管理安全性,驱动程序代码可以包含 IoValidateDeviceIoControlAccess 函数。 此功能允许驱动程序检查访问权限。 收到 IOCTL 后,驱动程序可以调用 IoValidateDeviceIoControlAccess,指定FILE_READ_ACCESS、FILE_WRITE_ACCESS 或两者。
实施精细 IOCTL 安全控制并不能取代使用上述技术管理驱动程序访问的需要。
有关详细信息,请参阅 定义 I/O 控制代码 和 I/O 控制代码的安全问题。
控制对纯软件驱动程序的访问
安全清单项 #4:如果要创建仅限软件的驱动程序,则必须实现其他访问控制。
纯软件内核驱动程序不使用即插即用 (PnP) 与特定硬件 ID 相关联,可以在任何电脑上运行。 这种驱动程序可用于除最初预期之外的其他目的,从而创建攻击途径。
由于纯软件内核驱动程序包含额外的风险,因此必须将其限制在特定硬件上运行(例如,通过使用唯一的 PnP ID 来创建 PnP 驱动程序,或通过检查 SMBIOS 表来检查是否存在特定硬件)。
例如,假设 OEM Fabrikam 想要分发一个驱动程序,以便为其系统启用超频工具。 如果此软件驱动程序在来自不同 OEM 的系统上执行,可能会导致系统不稳定或损坏。 Fabrikam 的系统应包含唯一的 PnP ID,以便创建也可以通过 Windows 更新更新的 PnP 驱动程序。 如果这不可能,并且 Fabrikam 正在编写一个旧版驱动程序,那么该驱动程序应找到另一种方法来验证它是否在 Fabrikam 系统上运行(例如,通过在启用任何功能之前检查 SMBIOS 表)。
遵循驱动程序安全编码准则
安全清单项 #5:查看代码并删除任何已知的代码漏洞。
创建安全驱动程序的核心活动是识别代码中需要更改的区域,以避免已知的软件漏洞。 其中许多已知的软件漏洞涉及对内存使用的严格监控,以避免其他程序覆盖或破坏驱动程序使用的内存位置。
代码扫描工具(如 CodeQL 和特定于驱动程序的测试)可用于帮助查找这些漏洞的一些,但不是全部。 本主题稍后将介绍这些工具和测试。
内存缓冲区
始终检查输入和输出缓冲区的大小,以确保缓冲区能够容纳所有请求的数据。 有关详细信息,请参阅无法检查缓冲区大小。
在将输出缓冲区返回给调用方之前,请用零正确初始化所有输出缓冲区。 有关详细信息,请参阅 输出缓冲区初始化失败。
验证长度可变的缓冲区。 有关详细信息,请参阅无法验证可变长度缓冲区。 有关使用缓冲区和使用 ProbeForRead 验证缓冲区地址的详细信息,请参阅 缓冲区处理。
使用适当的方法通过 IOCTL 访问数据缓冲区
Windows 驱动程序的主要职责之一是在用户模式应用程序和系统设备之间传输数据。 访问数据缓冲区的三种方法如下表所示。
IOCTL 缓冲区类型 | 总结 | 更多信息 |
---|---|---|
METHOD_BUFFERED | 建议用于大多数情况 | 使用缓冲 I/O |
METHOD_IN_DIRECT 或 METHOD_OUT_DIRECT | 在一些高速硬件输入/输出中使用 | 使用直接 I/O |
METHOD_NEITHER | 尽可能避免 | 既不使用缓冲输入输出,也不使用直接输入输出 |
通常,建议使用缓冲 I/O,因为它提供最安全的缓冲方法。 但即使使用缓冲 I/O,也存在风险,例如必须降低嵌入指针的风险。
有关在 IOCTLs 中使用缓冲区的详细信息,请参阅访问数据缓冲区的方法。
使用 IOCTL 缓冲 I/O 时出错
检查 IOCTL 相关缓冲区的大小。 有关详细信息,请参阅无法检查缓冲区大小。
正确初始化输出缓冲区。 有关详细信息,请参阅 输出缓冲区初始化失败。
正确验证长度可变的缓冲区。 有关详细信息,请参阅无法验证可变长度缓冲区。
使用缓冲 I/O 时,请确保在 IO_STATUS_BLOCK 结构信息字段中为 OutputBuffer 返回正确的长度。 不要仅仅直接从 READ 请求中返回长度。 例如,考虑这样一种情况:从用户空间返回的数据表明存在 4K 缓冲区。 如果驱动程序实际上只应返回 200 个字节,但却在信息字段中返回了 4K,则发生了信息泄露漏洞。 出现此问题的原因是,在早期版本的 Windows 中,I/O 管理器用于缓冲 I/O 的缓冲区不会为零。 因此,用户应用将获得原始的 200 字节数据加上缓冲区中 4K-200 字节的数据(非分页池内容)。 这种情况可能发生在所有使用缓冲系统 I/O 的场合,而不仅仅是使用 IOCTL 的场合。
IOCTL 直接 I/O 出错
正确处理零长度缓冲区。 有关详细信息,请参阅直接 I/O 出错。
引用用户空间地址时出错
验证嵌入在缓冲 I/O 请求中的指针。 有关详细信息,请参阅引用用户空间地址时出错。
使用 ProbeForRead等 API 验证用户空间中的任何地址,然后再尝试使用它。
驱动程序代码必须正确使用内存
所有驱动程序池分配都必须位于非可执行文件 (NX) 池中。 使用 NX 内存池本质上比使用可执行的非分页 (NP) 池更安全,并且可以更好地防止溢出攻击。
若要允许驱动程序支持 HVCI 虚拟化,还需要满足额外的内存要求。 有关详细信息,请参阅本文后面的实现 HVCI 兼容代码。
TOCTOU 漏洞
使用直接 I/O(用于 IOCTL 或读/写)时,可能存在潜在检查时间到使用时间 (TOCTOU) 漏洞。 请注意,当驱动程序正在访问用户数据缓冲区时,用户应用可以同时访问相同的数据缓冲区。
为了管理这种风险,将需要验证的任何参数从用户数据缓冲区复制到仅可从内核模式访问的内存中(如堆栈或池)。 然后,一旦用户应用程序无法访问数据,请验证并操作传入的数据。
特定于 MSR 模型的寄存器读取和写入
编译器内部函数(如 __readmsr 和 __writemsr)可用于访问特定于模型的寄存器。 如果需要此访问权限,驱动程序必须始终检查要读取或写入的寄存器是否受预期索引或范围的约束。
有关详细信息和代码示例,请参阅约束内核模式驱动程序中高特权行为的最佳做法中的提供读/写 MSR 的能力。
句柄
设备对象
IRP
Windows I/O 请求数据包(IRP)用于在操作系统和内核模式驱动程序之间通信 I/O 请求,从而封装数据包中的所有必要信息。 IRP 有助于异步数据传输、同步和错误处理,确保与硬件设备进行高效可靠的通信。 有关详细信息,请参阅 I/O 请求数据包 和 Windows I/O 模型概述。
WDF 和 IRP
使用 WDF 的优点之一是,WDF 驱动程序通常不直接访问 IRP。 例如,框架将表示读取、写入和设备 I/O 控制操作的 WDM IRP 转换为 KMDF/UMDF 在 I/O 队列中接收的框架请求对象。 强烈建议尽可能使用 WDF。
如果需要编写 WDM 驱动程序,请查看以下指南。
正确管理 IRP I/O 缓冲区
查看这些主题,这些主题介绍如何验证 IRP 输入值:
验证与 IRP 关联的值,例如缓冲区地址和长度。
如果您选择使用非 I/O,请注意,这与读取和写入不同,也与缓冲 I/O 和直接 I/O 不同。当使用非 I/O IOCTL 时,缓冲区指针和长度不会由 I/O 管理器进行验证。
正确处理 IRP 完成操作
除非驱动程序实际支持并处理 IRP,否则它永远不能用状态值 STATUS_SUCCESS
完成 IRP。 有关处理 IRP 完成操作的正确方法的信息,请参阅完成 IRP。
管理驱动程序 IRP 挂起状态
驱动程序在保存 IRP 之前应将 IRP 标记为挂起,并应考虑将对 IoMarkIrpPending 的调用和分配都包含在互锁序列中。 有关详细信息,请参阅无法检查驱动程序的状态和设备暂停时保持传入的 IRP。
正确处理 IRP 取消操作
取消操作可能很难正确编码,因为它们通常以异步方式执行。 处理取消操作的代码中的问题可能会在很长一段时间内被忽视,因为这段代码在运行中的系统中通常不会频繁执行。 请务必阅读并理解取消 IRP 下提供的所有信息。 请特别注意同步 IRP 取消和取消 IRP 时要考虑的要点。
最小化与取消操作相关的同步问题的一种推荐方法是实现可安全取消的 IRP 队列。
正确处理 IRP 清理和关闭操作
请确保了解 IRP_MJ_CLEANUP 和 IRP_MJ_CLOSE 请求之间的区别。 清理请求在应用程序关闭文件对象的所有句柄后到达,但有时在完成所有 I/O 请求之前到达。 关闭请求是在文件对象的所有输入/输出请求完成或取消后到达的。 有关详细信息,请参阅以下内容:
DispatchCreate、DispatchClose 和 DispatchCreateClose 例程
有关正确处理 IRP 的详细信息,请参阅处理 IRP 时出现其他错误。
使用安全功能
使用安全字符串函数。 有关详细信息,请参阅使用安全字符串函数。
使用安全算术函数。 有关详细信息,请参阅 安全整数库例程。
使用安全转换函数。 有关详细信息,请参阅 Kernel-Mode 安全整数函数的摘要。
其他安全问题
使用锁或互锁序列来防止争用条件。 有关详细信息,请参阅多处理器环境出错。
确保在安装或使用过程中驱动程序或关联的软件包未安装任何网络传输驱动程序接口(TDI)筛选器或分层服务提供商(LSP)。 请改用新式 API,例如 Windows 筛选平台 (WFP)。
其他代码漏洞
除了此处介绍的可能漏洞外,本文还提供了有关增强内核模式驱动程序代码安全性的其他信息:创建可靠的内核模式驱动程序。
有关 C 和 C++ 安全编码的其他信息,请参阅本文末尾的安全编码资源。
实现 HVCI 兼容代码
安全清单项 #6:验证驱动程序是否使用内存,使其与 HVCI 兼容。
内存完整性和 HVCI 兼容性
内存完整性(也称为虚拟机监控程序保护的代码完整性(HVCI)使用硬件技术和虚拟化将代码完整性(CI)决策功能与操作系统的其余部分隔离开来。 使用基于虚拟化的安全性来隔离 CI 时,内核内存成为可执行文件的唯一方法是通过 CI 验证。 这意味着内核内存页永远不能同时是可写的和可执行的(W+X),并且可执行代码不能直接被修改。
若要实现 HVCI 兼容代码,请确保驱动程序代码执行以下操作:
- 默认情况下选择加入 NX
- 使用 NX API/标志进行内存分配 (NonPagedPoolNx)
- 不要使用既可写又可执行的节
- 不要尝试直接修改可执行系统内存
- 不要在内核中使用动态代码
- 不要将数据文件加载为可执行文件
- 节对齐是 0x1000 (PAGE_SIZE) 的倍数。 例如,DRIVER_ALIGNMENT=0x1000
有关使用该工具和不兼容内存调用列表的详细信息,请参阅实现 HVCI 兼容代码。
有关相关系统基础知识安全测试的详细信息,请参阅 虚拟机监控程序代码完整性就绪状态测试和虚拟机监控程序保护的代码完整性 (HVCI)。
增强设备安装安全性
安全清单项 #7:查看驱动程序创建和安装指南,以确保遵循最佳做法。
创建安装驱动程序包的代码时,必须确保始终以安全方式安装设备。 安全设备安装是执行以下所有操作的过程:
- 限制对设备及其设备接口类的访问
- 限制对为设备创建的驱动程序服务的访问
- 保护驱动程序文件不被修改或删除
- 限制对设备的注册表项的访问
- 限制对设备的 WMI 类的访问
- 正确使用 SetupAPI 函数
有关详细信息,请参阅以下内容:
遵循特定技术的代码最佳实践
安全清单项 #8:请查看以下针对驱动程序的特定技术指导。
文件系统
有关文件系统驱动程序安全性的详细信息,请参阅以下内容:
Microsoft病毒计划
Microsoft病毒计划(MVI)可帮助组织改进客户赖以保护安全的安全解决方案。 Microsoft提供了工具、资源和知识,以支持性能、可靠性和兼容性更好的体验。 Microsoft与 MVI 合作伙伴协作,定义并遵循安全部署做法(SDP),以支持我们共同客户的安全和复原能力。
如果你是防病毒供应商,请参阅 Microsoft病毒计划 了解如何加入 MVI 以获取有关软件部署的更多帮助。 有关安全供应商如何更好地利用 Windows 的集成安全功能来提高安全性和可靠性的信息,请参阅 Windows 安全最佳做法,了解如何集成和管理安全工具。
NDIS - 网络
有关 NDIS 驱动程序安全性的信息,请参阅网络驱动程序的安全问题。
打印机
有关打印机驱动程序安全性的信息,请参阅 V4 打印机驱动程序安全注意事项。
Windows 图像采集 (WIA) 驱动程序的安全问题
有关 WIA 安全性的信息,请参阅 Windows 图像采集 (WIA) 驱动程序的安全问题。
将 SAL 注释添加到驱动程序代码
安全清单项 #9:向驱动程序代码添加 SAL 注释。
源代码注释语言 (SAL) 提供一组注释,可以用于描述函数如何使用其参数的,它关于参数的假设以及关于何时完成的保证。 注释是在头文件 sal.h
中定义的。 适用于 C++ 的 Visual Studio 代码分析使用 SAL 注释来修改其函数分析。 有关 SAL 2.0 for Windows 驱动程序开发的详细信息,请参阅 Windows 驱动程序的 SAL 2 批注和使用 SAL 注释减少 C/C++ 代码漏洞。
有关 SAL 的一般信息,请参阅 OSR 提供的本文。 SAL 批注: 不要恨我, 因为我美丽
执行对等代码评审
安全清单项 #10:执行对等代码评审,查找其他工具和进程未显示的问题
寻找知识丰富的代码评审者,查找你可能漏掉的问题。 另一双眼睛经常会看到你可能忽略的问题。
如果你没有合适的员工在内部查看代码,请考虑出于此目的参与外部帮助。
执行威胁分析
安全清单项 #11:修改现有驱动程序威胁模型或为驱动程序创建自定义威胁模型。
在考虑安全性时,一种常见方法是创建特定的威胁模型,试图描述可能的攻击类型。 这种技术在设计驱动程序时很有用,因为它迫使开发人员提前考虑针对驱动程序的潜在攻击途径。 确定了潜在的威胁之后,驱动程序开发人员就可以考虑防御这些威胁的方法,以增强驱动程序组件的整体安全性。
本文为创建轻型威胁模型提供了特定于驱动程序的指导:驱动程序的威胁建模。 本文提供了一个示例驱动程序威胁模型图,该图可用作您开发驱动程序威胁模型的起始参考点。
IHV 和 OEM 可以使用安全开发生命周期 (SDL) 最佳做法和相关工具来提高其产品的安全性。 有关详细信息,请参阅为 OEM 推荐的 SDL。
使用 CodeQL 检查驱动程序代码
安全清单项 #12:使用 CodeQL 检查驱动程序代码中的漏洞。
CodeQL(由 GitHub 提供)是一个语义代码分析引擎,并且广泛的安全查询套件与可靠的平台相结合,使其成为保护驱动程序代码的宝贵工具。 有关详细信息,请参阅 CodeQL 和静态工具徽标测试。
使用驱动验证器检查漏洞
安全清单项 #13:使用驱动程序验证程序检查驱动程序代码中的漏洞。
驱动程序验证程序使用一组接口规则和操作系统模型来确定驱动程序是否与 Windows 操作系统正确交互。 驱动程序验证程序在驱动程序代码中发现缺陷,这些缺陷可能指向驱动程序中的潜在 bug。
驱动程序验证程序允许对驱动程序进行实时测试。 驱动程序验证程序监视 Windows 内核模式驱动程序和图形驱动程序,目的是检测可能损坏系统的非法函数调用或操作。 附加的调试器允许实时查看 OS 和驱动程序代码执行。 驱动程序验证程序可以使 Windows 驱动程序经受各种压力和测试以发现不当行为。 有关详细信息,请参阅驱动程序验证程序。
驱动程序验证程序适用于 WDM 和 KMDF 驱动程序。 有关它可以检查的具体内容,请参阅以下主题。
有关驱动程序验证程序可以使用的驱动程序的详细信息,请参阅 DDI 合规性规则 和 支持的驱动程序。 有关特定类型的驱动程序的其他验证程序规则,请参阅:
若要熟悉 DV,可以使用其中一个示例驱动程序(例如特色烤箱示例:https://github.com/Microsoft/Windows-driver-samples/tree/main/general/toaster/toastDrv/kmdf/func/featured)。
通过硬件兼容性程序测试检查代码
安全清单项 #14:使用安全相关的硬件兼容性程序测试来检查安全问题。
硬件兼容性计划包括安全相关测试,可用于查找代码漏洞。 Windows 硬件兼容性计划利用 Windows 硬件实验室工具包 (HLK) 中的测试。 Windows HLK 设备基础功能测试可在命令行上使用,以练习驱动程序代码和探测弱点。 有关设备基础功能测试和硬件兼容性计划的一般信息,请参阅 Windows 硬件实验室工具包。
以下测试示例可能有助于检查驱动程序代码中与代码漏洞相关的某些行为:
还可以使用驱动程序验证程序随附的内核同步延迟模糊处理。
CHAOS(并发硬件和操作系统)测试同时运行各种 PnP 驱动程序测试、设备驱动程序模糊测试和电源系统测试。 有关详细信息,请参阅 CHAOS 测试(设备基础功能)。
设备基础功能渗透测试执行各种形式的输入攻击,这是安全测试的关键组成部分。 攻击和渗透测试可以帮助识别软件接口中的漏洞。 有关详细信息,请参阅渗透测试(设备基础功能)。
使用 Device Guard - 合规性测试以及本文中介绍的其他工具,以确认驱动程序是否与 HVCI 兼容。
自定义和领域特定的测试工具
考虑开发自定义域特定的安全测试。 若要开发其他测试,请从软件的原始设计者处收集输入,也可以从与软件设计无关但熟悉特定类型驱动程序的开发资源获取信息,并咨询一名或多名熟悉安全入侵分析和防护的人员。
使用 BinSkim 和 SignTool 等工具检查是否准备好交付驱动程序
安全清单项 #15:在将代码上传到合作伙伴中心之前,使用 BinSkim 和 SignTool 等工具检查已编译的代码。
使用 BinSkim 和 SignTool 等工具检查二进制文件,以在将代码上传到合作伙伴中心以使用 Windows 更新分发之前检查已编译的代码。 在提交二进制文件以供分发之前,具有用于检查已编译二进制文件的工具会添加另一层保护。
BinSkim
BinSkim 可以识别可能使二进制易受攻击的编码和生成做法。 BinSkim 检查以下项:
- 使用过时的编译器工具集 - 应尽可能使用最新的编译器工具集中编译二进制文件,以最大限度地利用当前的编译器级别和 OS 提供的安全缓解措施。
- 不安全的编译设置 - 二进制文件应使用最安全的设置进行编译,以启用 OS 提供的安全缓解措施,最大限度地减少编译器错误和可操作的警告报告等。
- 签名问题 - 已签名的二进制文件应使用加密强算法进行签名。
BinSkim 是一种开放源代码工具,它生成使用静态分析结果交换格式 (SARIF) 格式的输出文件。 BinSkim 取代了以前的 BinScope 工具。
有关 BinSkim 的详细信息,请参阅使用 BinSkim 检查二进制文件和 BinSkim 用户指南。
SignTool
使用 SignTool 检查发布签名的驱动程序文件。 有关详细信息,请参阅验证已进行发布签名的驱动程序文件的签名和验证通过商业发布证书签名的目录文件的签名。
不要生成签名测试代码
安全清单项 #16:不要生成代码对开发、测试和制造内核驱动程序代码进行签名。
用于开发、测试或制造的内核驱动程序代码可能包含会带来安全风险的危险功能。 绝不应用 Windows 信任的证书对这种危险的代码进行签名。 执行危险驱动程序代码的正确机制是禁用 UEFI 安全启动,启用 BCD“TESTSIGNING”,并使用不受信任的证书(例如,makecert.exe 生成的证书)对开发、测试和制造代码进行签名。
由受信任的软件发行者证书 (SPC) 或 Windows 硬件质量实验室 (WHQL) 签名进行签名的代码不得有助于绕过 Windows 代码完整性和安全技术。 在代码由受信任的 SPC 或 WHQL 签名进行签名之前,请先确保它符合创建可靠的内核模式驱动程序的指导。 此外,代码不得包含任何危险行为,如下所述。
危险行为的示例包括:
- 提供将任意内核、物理或设备内存映射到用户模式的功能。
- 提供读取或写入任意内核、物理或设备内存的功能,包括端口输入/输出 (I/O)。
- 提供对绕过 Windows 访问控制的存储的访问权限。
- 提供修改硬件或固件的功能,而这些硬件或固件并非驱动程序所设计管理的。
完成正确的发布驱动程序签名并通过 Windows 更新分发驱动程序包
安全清单项 #17:使用 Windows 合作伙伴门户提交驱动程序包,以便通过 Windows 更新进行分发和签名。
将驱动程序包发布到公众之前,请提交该包进行认证。 有关详细信息,请参阅 测试性能与兼容性 和 硬件程序入门。
强烈建议使用 Windows 更新来分发驱动程序包。 Windows Update 提供可靠、安全、全球范围且符合法规的分发系统,用来分发驱动程序更新。 有关详细信息,请参阅 分发驱动程序包。
使用逐步推出和 Windows 硬件合作伙伴中心中的驱动程序外部测试,在定义的 Windows 预览体验成员圈中分发驱动程序,同时提供自动监视和评估。 使用 Microsoft 驱动程序度量监视驱动程序的推出,例如没有内核模式崩溃的计算机所占的百分比,以维持质量。
有关安全软件部署做法的说明,请参阅 CISA 安全软件部署:软件制造商如何确保客户的可靠性。
了解如何通过 Microsoft 的易受攻击和恶意驱动程序报告中心 来报告驱动程序
安全清单项 #18:了解如何使用 Microsoft 易受攻击和恶意驱动程序报告中心报告驱动程序
任何人都可以使用 Microsoft易受攻击和恶意驱动程序报告中心提交可疑驱动程序。 有关如何提交驱动程序进行分析的信息,请参阅此博客文章 - 使用新的 Microsoft 易受攻击和恶意驱动程序报告中心提高内核安全性
报告中心可以扫描和分析为 x86 和 x64 体系结构生成的 Windows 驱动程序。 被检测为易受攻击和恶意的驱动程序将由Microsoft的易受攻击驱动程序团队标记出来,以便进行分析和调查。 确认是易受攻击的驱动程序后,会发出相应的通知,并将其添加到易受攻击的驱动程序阻止名单中。 有关详细信息,请参阅 Microsoft 推荐的驱动程序阻止规则。 这些规则默认应用于启用了虚拟机监控程序保护的代码完整性(HVCI)设备,以及运行 S 模式的 Windows 10。
查看安全编码资源
安全清单项 #19:查看这些资源,以加深你对适用于驱动程序开发人员的安全编码最佳做法的理解。
NIST 已知软件漏洞数据库
国家漏洞数据库(NVD)是安全相关软件缺陷(包括 Windows 驱动程序)的可搜索存储库。
安全编码标准
Carnegie Mellon University SEI CERT - C 编程标准:开发安全、可靠且保密的系统的规则(PDF)。
MITRE - CERT C 安全编码标准解决的弱点
安全编码组织
SAFECode - https://safecode.org/
OSR
OSR 提供驱动程序开发培训和咨询服务。 这些文章来自 OSR 时事通讯,重点介绍了驱动程序安全问题。
名称、安全描述符和设备类 - 使设备对象易于访问... and SAFE
驱动程序漏洞案例研究
从警报到驱动程序漏洞:Microsoft Defender ATP 调查发现了特权升级漏洞
软件供应链安全和软件材料清单(SBOM)
在 Microsoft 使用 SPDX 生成软件物料清单 (SBOM)
书籍
软件安全的24个致命罪:编程缺陷及其解决方法 一书,作者为迈克尔·霍华德、大卫·勒布朗克和约翰·维加
编写安全软件(第二版)作者:Michael Howard 和 David LeBlanc
软件安全评估的艺术:识别和预防软件漏洞作者:Mark Dowd、John McDonald 和 Justin Schuh
C 和 C++ 中的安全编码 (SEI 软件工程系列) 第二版,罗伯特·C·西科德
编程 Microsoft Windows 驱动程序模型(第二版),沃尔特·奥尼
使用 Windows Driver Foundation 开发驱动程序(开发人员参考)作者:Penny Orwick 和 Guy Smith
培训
Windows 驱动程序课堂培训可从以下供应商处获得:
安全编码在线培训可以从各种来源获得。 例如,本课程可从 coursera 上获取:
SAFECode 还提供免费培训:
专业认证
CERT 提供安全编码专业认证。
关键要点摘要
驱动程序安全性是一项复杂的任务,包含许多要素,但以下是需要考虑的几个关键点:
驱动程序存在于 Windows 内核中,在内核中执行时出现问题会暴露整个操作系统。 因此,请密切关注驱动程序安全,并在设计时考虑到安全性。
应用最低权限原则:
a. 使用严格的 SDDL 字符串来限制对驱动程序的访问
b. 进一步限制单个 IOCTL
创建一个威胁模型来识别攻击途径,并考虑是否可以进一步限制任何内容。
请注意从用户模式传入的嵌入指针。 它们需要在 try except 内进行探测和访问,而且它们容易出现检查使用时间 (ToCToU) 问题,除非捕获并比较缓冲区的值。
如果不确定,请使用 METHOD_BUFFERED 作为 IOCTL 缓冲方法。
使用代码扫描实用工具(如 CodeQL)查找已知的代码漏洞并修正任何已识别的问题。
寻找知识丰富的代码评审者,查找你可能漏掉的问题。
使用驱动程序验证程序并使用多个输入测试驱动程序,包括极端情况。